Commit 0796cf7
MM-65671: Agents CRUD, legacy bot migration, and bot account sync (#589)
* Add prescriptive Phase 1 plan: Database & Store Layer
Detailed implementation plan for Agents_UserAgents table, Morph migration,
CRUD store methods, and integration tests. Includes exact file paths,
line numbers, code snippets, and verification commands.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add UserAgent model, store CRUD, migration, and AgentStore interface
Phase 1 of self-service agent creation: Agents_UserAgents table with
Morph migration 000004, full CRUD store methods with JSON slice field
marshaling, AgentStore interface in api package, and comprehensive
integration tests (12 test functions covering round-trips, soft delete,
edge cases, and concurrency).
Also fixes setupTestStore to set search_path via connection string so
concurrent test goroutines use the correct schema, and updates migration
count assertion from 3 to 4.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add agent CRUD API endpoints, bot lifecycle, and license gating
Implements Phase 2 of self-service agent creation:
- Agent CRUD handlers (create, list, get, update, delete) with license gating
- Bot account lifecycle (create on agent create, deactivate on delete)
- Avatar upload endpoint for agent profile images
- Services listing endpoint (secrets stripped)
- Permission-based access control (create_agent permission, creator/admin checks)
- Partial update support via pointer fields in UpdateAgentRequest
- Comprehensive test coverage (9 test functions)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add runtime integration for DB-backed agents with HA cluster support
Implements Phase 3 of self-service agent creation:
- AgentStore interface in bots package for loading user agents from DB
- userAgentToBotConfig converter maps UserAgent to llm.BotConfig
- EnsureBots merges DB-backed agents into the live bot registry
- forceRefresh flag bypasses optimistic config-equality cache for agent CRUD
- clusterEventAgentUpdate event propagates agent changes to HA nodes
- ClusterAgentNotifier interface + refreshBotsAndNotify helper in API
- Agent create/update/delete handlers trigger registry refresh + cluster notify
- Runtime tests: converter, registry lookup, force-refresh flag
- Nil config guard in EnsureBots for test safety
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Phase 4: Add agent listing page, API client, and product registration
- Add TypeScript types for UserAgent, CreateAgentRequest, UpdateAgentRequest, ServiceInfo
- Add agent CRUD API client functions (getAgents, createAgent, updateAgent, deleteAgent, uploadAgentAvatar, getServices)
- Add Redux agents reducer for state caching
- Create AgentsPage root component with URL-based visibility at /plug/mattermost-ai/agents
- Create AgentRow component with avatar, name, tool badge, and actions menu
- Create AgentsList component with All/Your agents tabs, loading/error/empty states, and delete flow
- Create DeleteAgentDialog confirmation modal
- Create AgentsLicenseGate for E20+ license enforcement
- Register root component and main menu navigation entry point
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix getServices() URL and align EnabledTool TS type with Go struct
1. getServices() was calling /agents/services but the backend route is
/services on the base router — the gin router would match /agents/services
against /:agentid with agentid="services" → 404.
2. EnabledTool TS type had mismatched fields (type/id/name/serverName) vs
Go struct (server_origin/tool_name). Aligned to match backend.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add agent config modal with Configuration, Access, and MCPs tabs
Phase 5 implementation: three-tab modal for creating/editing agents.
- Config tab: display name, username, avatar, service selection, custom instructions
- Access tab: channel access, user access, agent admin controls
- MCPs tab: per-server and per-tool toggle switches with search
- Wire modal into agents_list.tsx for create and edit flows
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix UserAgent TS type to use snake_case matching Go JSON tags
The Go UserAgent struct uses snake_case JSON tags (bot_user_id,
creator_id, display_name, etc.) but the TS type used camelCase,
causing JSON deserialization to silently drop all fields.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add per-agent MCP tool filtering and access control wiring
Wire UserAgent.EnabledTools into the tool discovery pipeline so
DB-backed agents only expose their selected MCP tools. Adds
EnabledMCPTool type to BotConfig, convertEnabledTools mapping,
RetainOnlyMCPTools filter on ToolStore, and the wiring call in
getToolsStoreForUser. Nil = all tools allowed (backward compat),
empty = no MCP tools, populated = allowlist.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add E2E test infrastructure and specs for agent CRUD, access control, and MCP tools
Phase 7: Creates agent container factory, API helper, page object,
and three test spec files covering create/edit/delete flows, user
access level enforcement, and MCP tool selection.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Add search/filter, form validation, and edge case handling (Phase 8)
- Search input on listing page filters by display name and username
- Field-level validation errors in config modal with clear-on-edit
- Server-side 409 (username taken) mapped to inline field error
- Service deleted edge case: warning badge in agent row, fallback
option in service dropdown
- MCP server removed edge case: orphaned tools warning banner with
auto-cleanup on tab load
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix ServiceID column VARCHAR(26) too small for UUIDs
ServiceID stores UUIDs (36 chars) but was defined as VARCHAR(26),
causing POST /agents to fail with a Postgres truncation error.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix agents page navigation — add team prefix to route
navigateToAgentsPage() was pushing /plug/mattermost-ai/agents without
the team prefix, causing Mattermost's router to treat it as an unknown
team and redirect to "Team Not Found". Now extracts the current team
name from the URL path and builds the full team-scoped route.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Move Agents entry from team menu to product switcher
Use registerProduct (internal MM API) instead of registerMainMenuAction
so "Agents" appears in the product switcher grid icon rather than the
team dropdown menu. Simplify AgentsPage to a normal component — routing
is handled by registerProduct, so no URL-matching overlay is needed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert registerProduct, restore overlay approach with team-prefix fix
Reverts the registerProduct change (internal API not reliable) and
restores the original rootComponent overlay + registerMainMenuAction
approach. Fixes two issues in the original code:
1. navigateToAgentsPage() now extracts the current team name from the
URL and navigates to /${teamName}/plug/mattermost-ai/agents
2. AgentsPage visibility check uses includes() instead of endsWith()
to match team-prefixed URLs
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Revert "Revert registerProduct, restore overlay approach with team-prefix fix"
This reverts commit 7f7ba76.
* Validate username format and return proper status codes for agent creation errors
Add pre-validation for username format (must match ^[a-z][a-z0-9._-]*$)
returning 400 for invalid usernames. Detect duplicate username errors from
bot creation and return 409 Conflict instead of 500.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix global header losing dark theme on Agents product page
Add app__body and channel-view CSS classes on mount in AgentsPage,
matching what Playbooks and Boards do in their product components.
ChannelController normally sets these classes but isn't loaded in
product views, causing the header to render with a white background.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Fix MCP tool filtering bypass for agents with no tools enabled
SetEnabledToolsFromJSON was treating "[]" the same as "" (empty string),
mapping both to nil. This destroyed the nil-vs-empty distinction:
- nil EnabledTools = all tools allowed (config-defined bots)
- [] EnabledTools = no tools allowed (user disabled all toggles)
When an agent was saved with no MCP tools enabled, the DB stored "[]"
but on read it was converted to nil, bypassing the RetainOnlyMCPTools
filter in llm_context.go and allowing all tools at runtime.
Fix: only map "" (empty DB column from pre-feature agents) to nil.
Let json.Unmarshal handle "[]" → empty slice and "null" → nil correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* Filter RHS tools dropdown to show only agent's enabled MCP tools
The Tools popover in the RHS chat header was showing ALL available MCP
tool providers regardless of the selected agent's configured tools.
Changes:
- Backend: Add EnabledMCPTools to AIBotInfo so /ai_bots exposes each
bot's allowed MCP tools to the frontend
- Frontend: Add enabledMCPTools to LLMBot interface, pass it to
ToolProviderPopover, and filter the server list so only servers with
at least one enabled tool are shown
- Config-defined bots (null enabledMCPTools) continue to show all tools
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* E2E: expand mocked agents coverage, CI shard registration, RHS MCP harness
- Register tests/agents/*.spec.ts in e2e-shard-3; validate ci-test-groups
- crud: duplicate username text, search no-results, regular user listing, unprivileged 403 UI
- access-control: allowlist negative, UserAccessLevel None listing, delegated admin edit
- mcp-tools: RHS tool provider cases; longer container beforeAll timeouts
- api: RHS MCP filtering + tests; ai-plugin helpers for Tools menu
Made-with: Cursor
* E2E: stronger agent access + MCP assertions (API posts, Smocker body rules)
- MattermostPage: assert DM outcomes via bot posts (Client4) instead of thread reply UI
- access-control: use expectNoBotDmReplyFromApi / expectBotDmReplyFromApi
- openai-mock: buildChatCompletionMockRule for ordered body matchers
- mcp-tools: layered Smocker rules to tie responses to read_post in completion payload
Made-with: Cursor
* E2E: hold full negative DM window for no-expected-bot-reply (45s)
- expectNoBotDmReplyFromApi: poll until observeDurationMs elapses (default 45s, matches positive reply timeout)
- Remove early return after minObserve; extend block/allowlist-negative tests to 120s
Made-with: Cursor
* E2E: tighten MCP enabled tool assertions
Ensure the positive enabled-tools flow only passes after a mocked tool-call round trip, so the suite proves runtime MCP execution instead of just mocked response text.
Made-with: Cursor
* MM-65671: Self-service agents parity, system console cleanup, legacy migration
- Persist model, vision, tools, native tools, reasoning, structured output on user agents (DB, API, runtime bot mapping).
- Expose ServiceInfo fields and POST /agents/models/fetch for server-side model listing.
- Agents modal Configuration tab: full parity with legacy bot form; MCPs tab disabled when tools off.
- Structured output vs extended thinking: mutual exclusion, restore reasoning when structured off, inline note.
- Modal scroll fixes (min-height) for long config forms; service selection hint for advanced options.
- System Console: replace AI Bots editor with moved notice + link; default bot from getAIBots().
- Idempotent startup migration from config.bots to DB agents; sysadmin can manage migrated agents (empty creator).
- Tests: API/store, E2E crud and system console; skip legacy bot UI specs pending agents coverage.
- Export NativeToolsItem from bot.tsx for reuse.
Made-with: Cursor
* MM-65671: restore migrated agent coverage and config refresh
Add agent-builder coverage for the provider-specific settings that moved out of the system console, and rerun legacy config-bot migration after later config updates so seeded bots still appear in the UI.
Made-with: Cursor
* MM-65671: address code review findings and cleanup
Apply fixes from CodeRabbit review: add username validation on agent
update, guard against nil config in service validation, bound avatar
upload size (10MB / 413), align SetEnabledNativeToolsFromJSON empty-slice
semantics, remove legacy bot-reasoning-config spec (coverage moved to
provider-config), add optional chaining in e2e afterAll teardown, and
improve webapp accessibility (avatar alt text, dialog ARIA attributes).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* MM-65671: Agents CRUD, legacy bot migration, and bot account sync
- Extend user agent model, API, store, and bots integration
- Migrate legacy config bots to user agents on plugin enable
- Sync Mattermost bot display name and deactivate on agent update/delete
- Update agents UI, system console config, and i18n strings
- Add permission helper and e2e agent CRUD coverage
- Fix golangci errcheck and shadow issues in agent handlers and migration
Made-with: Cursor
* Stop tracking .planning; ignore planning directory
Remove .planning/phase-1/PLAN.md from version control (keep local copy).
.gitignore: use .planning pattern for the planning directory.
Made-with: Cursor
* MM-65671: Address PR review (API auth, bots, UI, i18n)
- Gate service list and model fetch with canConfigureAgentServices (own or manage-others agents)
- Reject agent username changes after create; validate service_id on update; log rollback bot deactivation failures
- EnsureBots fails closed when ListAgents errors; defer legacy bot migration when bots are missing
- Partial unique index on BotUserID for active agents; clone mock store agents in API tests
- Webapp: services error handling, delete in-flight guard, dialog focus, stale model fetch, MCP a11y, system console bots
- E2E: neutral agents page ready check; resilient access-control teardown
- i18n extract for new FormattedMessage strings
Made-with: Cursor
* MM-65671: Address follow-up CodeRabbit (MCP auth filter, HA notify, e2e locators)
Made-with: Cursor
* e2e: scope agent delete action to row menu
Use row-scoped locator for Delete in agent actions (match Edit pattern)
and pass display name from crud test.
Made-with: Cursor
* e2e: scope delete confirm button to delete-agent dialog
Use role dialog + Delete agent name; chain Delete button from dialog locator.
Made-with: Cursor
* MM-65671: Fix E2E for migrated agents and product route
- BotConfigHelper: resolve bots via GET /agents when legacy config bots are
empty after migration; update migrated agents via PUT /agents/:id.
- Grant manage_own_agent and manage_others_agent via mmctl in plugincontainer
(and manage_others_agent in agent-container) so admin and tests can call
agent APIs after legacy migration.
- Use a leading slash on AGENTS_ROUTE for registerProduct routing.
Made-with: Cursor
* MM-65671: Self-service agents E2E and agent builder draft sync
- Allow system admins to configure services, create agents, and manage
migrated legacy bots (empty CreatorID) via API.
- E2E: centralize mmctl agent permissions; grant after system-console
admin setup; live-service spec grants permissions for default admin.
- Agents list: show actions for migrated agents when user has manage_system.
- System console bot: data-testid on native tool checkboxes for E2E.
- Agent builder config tab: skip false service-change resets when the modal
hydrates from an empty serviceId; when switching services of the same
LLM type, preserve enabledNativeTools and reasoning fields (clear them
only when the provider type changes).
Made-with: Cursor
* MM-65671: Gate Create agent on canCreate; fix permission E2E
- Show Create agent only when the user has manage_own_agent or manage_system,
matching POST /agents authorization (manage_others alone cannot create).
- Add mmctl helper to revoke manage_own_agent from system_user for tests.
- Rewrite denied-create E2E: users without service list access cannot fill the
modal; assert the Create button is hidden after revoking the role permission.
Made-with: Cursor
* e2e: stabilize smart-reactions wait on plugin /react response
- Use Promise.all to register waitForResponse with the menu click.
- Match completed POST responses to the plugin react URL without requiring
response.ok() so Chromium still resolves when the handler returns a
non-2xx status.
- Bump test timeout and reaction visibility wait for slower CI.
Made-with: Cursor
* fix: eslint lines-around-comment and operator-linebreak in agents UI
Made-with: Cursor
* e2e: retry Smart Reactions basic tests once; extend reaction wait
CI sometimes completes /react before the reaction UI appears; align with
flake policy and give the reactions container up to 60s.
Made-with: Cursor
* e2e: fix smart reactions mock and default plugin LLM routing
- Set useResponsesAPI false and enabledNativeTools [] on default mock services/bots
so react flow uses chat completions (Smocker mocks) like other E2E containers.
- Use buildTextResponse for thumbsup SSE to match proven streaming shape.
Made-with: Cursor
* fix: prefer configured default bot for post actions
Use the configured default bot when post action endpoints are called without a bot username so migrated agent ordering does not route requests to the wrong service. Add a regression test that proves we honor config over in-memory bot slice order.
Made-with: Cursor
* e2e: give follow-ups login more headroom
Allow the first action-item extraction test more time to reach the channel view on slower CI containers so shard-1 does not fail before the scenario starts.
Made-with: Cursor
* Adjust product row style
* Address crspeller review: agent API dedupe, EnsureBots snapshot, MCP tool types, shared confirm dialog
Made-with: Cursor
* Port PR 617 MCP and OpenAI defaults.
Keep the self-serve agent flow aligned with the merged master changes by making MCP always available, preserving OpenAI responses defaults across the UI and API, and covering the updated behavior in tests.
Made-with: Cursor
* Add refetch for agents list on creation, fix defaults for structured json conflicting with extended reasoning
* Fix MCP tool tests for native tool defaults.
Keep the new default web search behavior intact by making the MCP-focused e2e agents explicitly disable native tools, and align bifrost expectations with the current empty-slice filtering behavior.
Made-with: Cursor
* Address remaining PR feedback.
Deduplicate agent service validation, add a Manage agents action, and remove low-signal bot tests.
Made-with: Cursor
* Refactor UserAgent to use old BotConfig
* Make the UI the source of truth for agent defaults and use explicit full-object create/update payloads.
- Remove the unshipped SelfServiceAgentDefaults config and its create-time
default injection so the create modal owns all defaults.
- Convert CreateAgentRequest / UpdateAgentRequest to full-object shapes;
update is now a full replacement rather than a pointer-based patch.
- Make enabledMCPTools a required tri-state field on both endpoints
(null = all, [] = none, [items] = allowlist; omission is rejected).
- Drop defaultControlledFieldsTouched plumbing from the create modal so
both create and update send the full draft on every save.
- Update tests and e2e helpers for the new contract; e2e updateAgent
helper fetches + merges to preserve partial-override ergonomics.
Made-with: Cursor
* e2e: fix BotConfigHelper agent updates for full-object PUT contract
Export mergeAgentIntoUpdate and use GET+merge+PUT for migrated bots so
partial displayName/service updates match the agents API after the
pointer-patch removal.
Made-with: Cursor
* Replace per-agent MCP tri-state with explicit auto-enable boolean
The per-agent `enabledMCPTools` field was overloaded with tri-state
semantics (nil=all, []=none, [..]=allowlist) to preserve the pre-allowlist
"all MCP tools" behavior for migrated config bots. That coupling required
a custom JSON unmarshaler, a presence-tracked `enabledMCPToolsProvided`
bool, special marshal/unmarshal in the store, and `null` vs `"[]"`
distinctions in SQL — all to encode a single user intent: "let this agent
pick up every MCP tool, now and in the future."
Replaces the tri-state with an explicit `AutoEnableNewMCPTools bool`
on BotConfig. When true, the agent gets every MCP tool (existing and
future) and `EnabledMCPTools` is ignored. When false, only tools listed
in `EnabledMCPTools` are available.
Key changes:
- BotConfig: add AutoEnableNewMCPTools; drop tri-state doc on EnabledMCPTools.
- Store: extend migration 000005 with the new column; simplify
marshal/unmarshal to drop nil/"null" handling.
- API: remove custom UnmarshalJSON + enabledMCPToolsProvided and the 400
guard that required the field to be present. Expose the new bool in
AIBotInfo for the RHS popover.
- Legacy bot migration: flip AutoEnableNewMCPTools=true for config-migrated
agents to preserve their historical "all tools" behavior.
- Frontend: add a checkbox to the MCPs tab ("Automatically enable all MCP
tools"), default new agents to auto-enable on (fixes the prior UX quirk
where new agents defaulted to zero tools), and surface "All MCP tools"
as an agent row badge.
- E2E: update types and tests; the "implicit-all" case now asserts the
bool directly instead of a null allowlist.
- Makefile: quote the i18n-extract glob so formatjs recurses into
src/components/**/tabs/ (macOS bash 3.2 lacks globstar).
None of the tri-state behavior has shipped, so no backfill migration is
needed — the original 000005 migration is edited directly.
Made-with: Cursor
* MM-65671: Agent API hardening and themed react-select
- Cap agent create/update JSON bodies and validate config before bot creation.
- Allow one self-service agent without multi-LLM license; keep enterprise unlimited.
- Add active agent count for quota checks and extend API tests.
- Portal async multi-select menus to #root with fixed positioning and high z-index
so agent Access pickers are not clipped by the modal footer.
- Theme SelectUser/SelectChannel and system console ComboboxItem with
center-channel tokens for dark/light consistency.
- Align reasoning config and related LLM configuration with branch work.
Made-with: Cursor
* Address Cursor Bugbot: EnsureBots DB agents, bot refresh fallbacks, DRY Responses API
- Skip deactivating plugin-owned Mattermost bots that still map to active DB
agents when EnsureBots cannot reconcile them (missing/invalid service).
- Centralize ServiceUsesResponsesAPI on llm.ServiceConfig; bifrost and API use it.
- refreshBotsAndNotify returns EnsureBots error; update/delete handlers only
apply explicit Bot.Patch / UpdateActive when EnsureBots did not run, failed,
or no bot registry is configured.
GitHub: replied on PR #589 to Cursor auth threads (handlers use canConfigureAgentServices).
Made-with: Cursor
* Fix nil a.bots in canUserAccessAgent via UsageRestrictionsForUserConfig
Agent list/get routes call canUserAccessAgent without aiBotRequired; dereferencing
a.bots for usage checks could panic when the bot registry is nil. Evaluate
restrictions with the plugin client instead; MMBots delegates to the same helper.
Made-with: Cursor
* Fix eslint lines-around-comment in select.tsx
Made-with: Cursor
* Fix Bugbot: clone config on GET admin/config; ShouldBindJSON for models/fetch
- Avoid mutating cached Services when normalizing OpenAI UseResponsesAPI for the response.
- Use ShouldBindJSON in handleFetchModelsForService for consistent error handling.
- Add regression test for GET not mutating stored service config.
Made-with: Cursor
* store: monotonic UpdateAt on agent update (fix CI flake)
plugin-tests failed on TestAgentUpdate when CreateAgent and UpdateAgent
shared the same millisecond: assert.Greater(UpdateAt) failed. Bump UpdateAt
to at least previous+1 when the clock has not advanced.
Made-with: Cursor
* e2e: align live service agent tool settings
Update the live service flow to reset agent tool defaults after creation so the DM verification exercises plain replies instead of hanging on pending tool calls.
Made-with: Cursor
* style: gofmt api_agents.go
Normalize formatting in api_agents.go so the Go lint/style checks pass cleanly.
Made-with: Cursor
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>1 parent fa4910a commit 0796cf7
97 files changed
Lines changed: 9457 additions & 1291 deletions
File tree
- api
- bifrost
- bots
- config
- conversations
- e2e
- helpers
- scripts
- tests
- action-item-extraction
- agents
- smart-reactions
- system-console
- llmcontext
- llm
- mcp
- server
- store
- migrations
- webapp/src
- components
- agents
- tabs
- assets
- rhs
- system_console
- i18n
- types
- utils
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
51 | 51 | | |
52 | 52 | | |
53 | 53 | | |
54 | | - | |
| 54 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
444 | 444 | | |
445 | 445 | | |
446 | 446 | | |
447 | | - | |
| 447 | + | |
448 | 448 | | |
449 | 449 | | |
450 | 450 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
67 | 67 | | |
68 | 68 | | |
69 | 69 | | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
70 | 81 | | |
71 | 82 | | |
72 | 83 | | |
| |||
77 | 88 | | |
78 | 89 | | |
79 | 90 | | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
80 | 96 | | |
81 | 97 | | |
82 | 98 | | |
| |||
99 | 115 | | |
100 | 116 | | |
101 | 117 | | |
| 118 | + | |
102 | 119 | | |
103 | 120 | | |
| 121 | + | |
104 | 122 | | |
105 | 123 | | |
106 | 124 | | |
| |||
126 | 144 | | |
127 | 145 | | |
128 | 146 | | |
| 147 | + | |
129 | 148 | | |
130 | 149 | | |
| 150 | + | |
131 | 151 | | |
132 | 152 | | |
133 | 153 | | |
| |||
152 | 172 | | |
153 | 173 | | |
154 | 174 | | |
| 175 | + | |
155 | 176 | | |
156 | 177 | | |
| 178 | + | |
157 | 179 | | |
158 | 180 | | |
159 | 181 | | |
| |||
212 | 234 | | |
213 | 235 | | |
214 | 236 | | |
| 237 | + | |
| 238 | + | |
| 239 | + | |
| 240 | + | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
215 | 253 | | |
216 | 254 | | |
217 | 255 | | |
| |||
295 | 333 | | |
296 | 334 | | |
297 | 335 | | |
298 | | - | |
299 | 336 | | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
300 | 341 | | |
301 | 342 | | |
302 | 343 | | |
| |||
351 | 392 | | |
352 | 393 | | |
353 | 394 | | |
354 | | - | |
355 | | - | |
356 | | - | |
357 | | - | |
358 | | - | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
| 395 | + | |
| 396 | + | |
| 397 | + | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
| 403 | + | |
| 404 | + | |
| 405 | + | |
363 | 406 | | |
364 | 407 | | |
365 | 408 | | |
| |||
392 | 435 | | |
393 | 436 | | |
394 | 437 | | |
395 | | - | |
396 | | - | |
397 | | - | |
398 | | - | |
399 | | - | |
400 | | - | |
401 | | - | |
402 | | - | |
403 | | - | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
| 441 | + | |
| 442 | + | |
| 443 | + | |
| 444 | + | |
| 445 | + | |
| 446 | + | |
| 447 | + | |
| 448 | + | |
404 | 449 | | |
405 | 450 | | |
406 | 451 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
242 | 242 | | |
243 | 243 | | |
244 | 244 | | |
245 | | - | |
246 | | - | |
247 | | - | |
248 | | - | |
249 | | - | |
250 | | - | |
251 | | - | |
252 | | - | |
253 | | - | |
254 | | - | |
255 | | - | |
256 | | - | |
257 | | - | |
258 | | - | |
259 | | - | |
260 | | - | |
261 | | - | |
262 | | - | |
263 | | - | |
| 245 | + | |
| 246 | + | |
| 247 | + | |
| 248 | + | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
264 | 253 | | |
265 | | - | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
| 259 | + | |
| 260 | + | |
| 261 | + | |
266 | 262 | | |
| 263 | + | |
| 264 | + | |
267 | 265 | | |
268 | 266 | | |
269 | 267 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
0 commit comments