-
Completed externalagent foundation phase 10 (shared resolve orchestrator extraction):
- extracted shared externalagent resolve/approval policy handling into
pkg/externalagent/orchestrator.go. - WebUI now uses the shared orchestrator for launch-policy preview and approval gating instead of carrying its own private duplicate logic.
- extracted shared externalagent resolve/approval policy handling into
-
Verification run:
go test -count=1 ./pkg/webui -run 'TestHandleResolveExternalAgentSession(CreatesSession|IncludesMatchedPermissionRulePreview|ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByPermissionRule|StartsProcessWhenApproved)|TestApproveExternalAgentPendingRequest(StartsProcessImmediately|AllowsSubsequentResolveForSameSession)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 13 (gateway shared starter adoption):
- gateway externalagent resolve now also uses the shared externalagent process starter path, so approved gateway launches can immediately create a runtime process instead of stopping at session resolution.
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestResolveExternalAgentSessionEndpoint(CreatesSession|ResolvesRelativeWorkspace|RejectsWorkspaceOutsideConfiguredRoot|IncludesMatchedPermissionRulePreview|StartsProcessWhenApproved)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 12 (shared process starter extraction):
- extracted the common externalagent process-start path into
pkg/externalagent/starter.go. - WebUI now uses the shared starter instead of keeping a private process-start implementation, making the spawn/continue layer align with the shared resolve orchestrator direction.
- extracted the common externalagent process-start path into
-
Verification run:
go test -count=1 ./pkg/externalagent ./pkg/webui -run 'TestHandleResolveExternalAgentSession(CreatesSession|IncludesMatchedPermissionRulePreview|ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByPermissionRule|StartsProcessWhenApproved)|TestApproveExternalAgentPendingRequest(StartsProcessImmediately|AllowsSubsequentResolveForSameSession)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 11 (gateway shared orchestrator adoption):
- gateway externalagent resolve now uses the shared
pkg/externalagent/orchestrator.gopreview path instead of keeping a private duplicate of launch-policy evaluation logic. - this is the first cross-consumer adoption of the shared orchestrator layer.
- gateway externalagent resolve now uses the shared
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestResolveExternalAgentSessionEndpoint(CreatesSession|ResolvesRelativeWorkspace|RejectsWorkspaceOutsideConfiguredRoot|IncludesMatchedPermissionRulePreview)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 9 (approval handler direct continue):
- approving an externalagent-origin pending request now immediately calls the same externalagent process-start seam, so approval itself continues launch without waiting for another resolve request.
-
Verification run:
go test -count=1 ./pkg/webui -run 'Test(ApproveExternalAgentPendingRequestStartsProcessImmediately|ApproveExternalAgentPendingRequestAllowsSubsequentResolveForSameSession|HandleResolveExternalAgentSessionStartsProcessWhenApproved)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 8 (immediate continue-on-approve):
- approving an externalagent-origin pending request now immediately continues the launch by starting the session process, instead of waiting for a second resolve call.
- this removes the last obvious two-step approval awkwardness from the WebUI externalagent flow.
-
Verification run:
go test -count=1 ./pkg/webui -run 'Test(ApproveExternalAgentPendingRequestStartsProcessImmediately|ApproveExternalAgentPendingRequestAllowsSubsequentResolveForSameSession|HandleResolveExternalAgentSessionStartsProcessWhenApproved)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 7 (process spawn/continue seam):
- WebUI externalagent resolve now attempts to start the session process once the launch is approved or otherwise not blocked by approval.
- subsequent resolve on an approved session now reuses the same session and ensures the runtime process is started if it is not already running.
- kept the slice intentionally narrow: this is a shared start-attempt seam with launch metadata/event persistence, not a full cross-consumer externalagent orchestrator.
-
Verification run:
go test -count=1 ./pkg/webui -run 'Test(HandleResolveExternalAgentSessionStartsProcessWhenApproved|ApproveExternalAgentPendingRequestStartsProcessOnSubsequentResolve|HandleResolveExternalAgentSessionReturnsPendingApprovalForAskRule|HandleResolveExternalAgentSessionRejectsDeniedByPermissionRule)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent consumer phase 3 (gateway consumer wiring):
- gateway now exposes
/api/v1/external-agents/resolve-sessionas the next real externalagent consumer. - this path reuses the same externalagent launch normalization, so canonical launcher allowlist and workspace policy now apply on gateway-owned session resolution too.
- gateway now exposes
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestResolveExternalAgentSessionEndpoint(CreatesSession|ResolvesRelativeWorkspace|RejectsWorkspaceOutsideConfiguredRoot|RejectsCommandMismatch)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 6 (approve-after-pending resume seam):
- approving an externalagent-origin pending request now upgrades that session into
approval.ModeAuto, so the same session can be resolved again without immediately re-entering the same pending gate. - kept the seam intentionally small: this is a session-level approval override for the externalagent resolve path, not a full process spawn/resume orchestrator.
- approving an externalagent-origin pending request now upgrades that session into
-
Verification run:
go test -count=1 ./pkg/webui -run 'TestHandleResolveExternalAgentSession(ApproveExternalAgentPendingRequestAllowsSubsequentResolveForSameSession|ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByPermissionRule)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent consumer phase 2 (WeChat codex consumer wiring):
- WeChat
codexruntime creation now reuses externalagent launch normalization, so workspace policy and launcher allowlist apply on the first non-WebUI consumer path. - this is the first real channel consumer beyond the WebUI resolve endpoint.
- WeChat
-
Verification run:
go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent foundation phase 5 (real pending approval flow):
- upgraded WebUI externalagent resolve from a read-only policy preview to a minimal real approval bridge.
- permission rule
denynow rejects the launch with403; permission ruleaskor fallback manual approval mode now return202with a pending approval request ID and sync session pending-state intotaskStore. - kept the slice intentionally narrow: session creation still happens first, and this flow does not yet execute a downstream external-agent process or add approve/deny resume behavior for consumers.
-
Verification run:
go test -count=1 ./pkg/webui -run 'TestHandleResolveExternalAgentSession(ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByPermissionRule|CreatesSession|IncludesMatchedPermissionRulePreview)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webuipassed.
-
Completed gateway control-plane hardening phase 21 (member remote_addr redaction):
- member-scoped control-plane reads now redact
remote_addrfrom connection list/detail payloads, while admin/owner visibility stays unchanged. - kept this as response-level redaction only; no connection metadata model changes.
- member-scoped control-plane reads now redact
-
Verification run:
go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webuipassed.
-
Completed externalagent foundation phase 4 (read-only permission/elicitation bridge):
- extended
/api/external-agents/resolve-sessionwith a read-onlylaunch_policypreview so callers can see the current externalagent launcher'sapproval_modeand permission-rule match result without starting a real approval flow. - kept the slice intentionally narrow: no real approval request is enqueued yet, and no channel/gateway runtime consumer behavior changed.
- extended
-
Verification run:
go test -count=1 ./pkg/webui -run 'TestHandleResolveExternalAgentSession(CreatesSession|IncludesMatchedPermissionRulePreview|RejectsUnknownAgentKind|RejectsToolMismatchForKnownAgentKind|RejectsCommandThatDoesNotMatchTool)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webuipassed.
-
Completed gateway control-plane hardening phase 20 (bulk-delete remaining visibility):
- refined
DELETE /api/v1/connectionssoremainingnow reports the caller-visible remaining connection count instead of the global live connection count. - this closes a small information leak where
memberusers could infer other users' live gateway connections from the bulk-delete response body.
- refined
-
Verification run:
go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webuipassed.
-
Completed externalagent foundation phase 3 (launcher allowlist):
- locked
/api/external-agents/resolve-sessionandexternalagent.Managerto a fixed launcher identity allowlist instead of treating the entrypoint as a generic shell-command resolver. - supported
agent_kindlaunchers are nowcodex,claude,opencode, andaider; blanktool/commandstill default from the canonical launcher, while explicit mismatches are rejected with400. - kept the slice intentionally narrow: no args policy, no aliases, no channel/gateway consumer wiring yet.
- locked
-
Verification run:
go test -count=1 ./pkg/externalagent ./pkg/webui -run 'Test(ResolveSessionRejectsCommandThatDoesNotMatchTool|ResolveSessionAllowsCommandThatMatchesTool|ResolveSessionRejectsUnknownAgentKind|ResolveSessionRejectsToolMismatchForKnownAgentKind|HandleResolveExternalAgentSessionRejectsCommandThatDoesNotMatchTool|HandleResolveExternalAgentSessionRejectsUnknownAgentKind|HandleResolveExternalAgentSessionRejectsToolMismatchForKnownAgentKind)$'passed.go test -count=1 ./pkg/externalagent ./pkg/webuipassed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webuipassed.
-
Completed externalagent foundation phase 2 (workspace policy gate):
- made
externalagent.Managerconfig-aware so external-agent session resolution can enforce workspace policy instead of trusting arbitrary absolute paths. - blank
workspacenow defaults tocfg.WorkspacePath(), and relativeworkspacevalues now resolve under that configured root before session identity/reuse checks. - when
agents.defaults.restrict_to_workspace=true, resolved workspaces must stay insidecfg.WorkspacePath(); disabling that flag keeps the existing absolute-path behavior. - kept the slice intentionally narrow: no channel/gateway wiring yet, only manager/WebUI policy enforcement and regression coverage.
- made
-
Verification run:
go test -count=1 ./pkg/externalagent ./pkg/webui -run 'Test(ResolveSessionRejectsWorkspaceOutsideConfiguredRootWhenRestricted|ResolveSessionAllowsWorkspaceOutsideConfiguredRootWhenRestrictionDisabled|HandleResolveExternalAgentSessionRejectsWorkspaceOutsideConfiguredRoot|HandleResolveExternalAgentSessionCreatesSession|HandleResolveExternalAgentSessionReusesExistingSession)$'passed.go test -count=1 ./pkg/externalagent ./pkg/webuipassed.
-
Completed gateway control-plane hardening phase 19 (member single-connection delete parity):
- aligned
DELETE /api/v1/connections/{id}with the already-landed bulk-delete member semantics. membercan now delete only their own live gateway connection; attempts against other users or missing targets return404to avoid leaking existence.- kept admin/owner semantics unchanged and reused the existing gateway auth/IP/rate-limit boundary instead of introducing new pairing or ownership persistence.
- aligned
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(DeleteConnectionEndpoint(RemovesClient|ReturnsNotFoundForUnknownClient|ReturnsNotFoundForMemberWithoutOwnedTarget|RejectsMemberRoleForOtherUsersConnection|RequiresAuth)|DeleteConnectionsEndpoint(RemovesAllClientsForAdmin|AllowsMemberRoleForOwnedClientsOnly|DeletesOwnedClientsWhenMemberHasOnlyOwnedConnections|RequiresAuth))$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed gateway control-plane hardening phase 18 (pairing source breakdown in status):
- extended
/api/v1/statuswithpaired_generated_connections,paired_requested_connections, andpaired_legacy_connections. - kept the slice read-only and derived from existing live connection/session-source classification without introducing new pairing persistence.
- extended
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(StatusEndpointReportsPairingSourceBreakdown|StatusEndpointReportsPairedConnections|StatusEndpointCountsConnectionsDeterministically)$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Continued WeChat presenter/interaction protocol follow-up:
- aligned the skill-install confirmation prompt copy with the newly supported
/select 1and/select 2aliases. - kept the slice intentionally narrow: copy-only alignment on top of the already-landed
/selectalias behavior.
- aligned the skill-install confirmation prompt copy with the newly supported
-
Verification run:
go test -count=1 ./pkg/channels/wechat -run 'Test(ParseWeChatInteractionAction|ResolvePendingInteraction(Confirm|Deny|SelectConfirmAlias|SelectDenyAlias)|FormatWeChatPrompt(IncludesSelectAliasesForSkillInstall)?)'passed.
-
Continued WeChat presenter/interaction protocol follow-up:
- extended the existing skill-install confirmation flow so
/select 1now aliases confirm and/select 2aliases deny, instead of rejecting all select actions as unsupported. - kept the slice intentionally narrow: only the existing skill-install confirmation path changed, without broadening into generic multi-option interaction state.
- extended the existing skill-install confirmation flow so
-
Verification run:
go test -count=1 ./pkg/channels/wechat -run 'TestResolvePendingInteractionSelect(ConfirmAlias|DenyAlias)$'passed.go test -count=1 ./pkg/channels/wechat -run 'Test(ParseWeChatInteractionAction|ResolvePendingInteraction(Confirm|Deny|SelectConfirmAlias|SelectDenyAlias|DelegatesToRuntimeApprovals)|FormatWeChatPrompt|ControlService(BindRuntimeRejectsEmptyChatIDBeforeBinding|SendToRuntimeRejectsEmptyChatIDBeforeExplicitRouting|CreateRuntimeRejectsEmptyChatIDBeforeCreatingSession))'passed.
-
Completed Slack interactive flow phase 8:
- added a seventh real shortcut/modal business closure:
startshortcut now opens a modal and submission reuses the existing/startcommand semantics. - kept the slice intentionally narrow by treating the modal as a thin shell over the existing command path instead of inventing Slack-only onboarding behavior.
- added a seventh real shortcut/modal business closure:
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpensStartModal|ViewSubmissionExecutesStartCommand)$'passed.go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpens(FindSkills|Settings|Model|Help|Status|Agent|Start)Modal|ViewSubmissionExecutes(FindSkills|Settings|Model|Help|Status|Agent|Start)Command)$'passed.
-
Completed Slack interactive flow phase 7:
- added a sixth real shortcut/modal business closure:
agentshortcut now opens a modal and submission reuses the existing/agentcommand semantics. - kept the slice intentionally narrow by treating the modal as a thin shell over the existing command path instead of inventing Slack-only agent behavior.
- added a sixth real shortcut/modal business closure:
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpensAgentModal|ViewSubmissionExecutesAgentCommand)$'passed.go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpens(FindSkills|Settings|Model|Help|Status|Agent)Modal|ViewSubmissionExecutes(FindSkills|Settings|Model|Help|Status|Agent)Command)$'passed.
-
Completed Slack interactive flow phase 6:
- added a fifth real shortcut/modal business closure:
statusshortcut now opens a modal and submission reuses the existing/statuscommand semantics. - kept the slice intentionally narrow by treating the modal as a thin shell over the existing command path instead of inventing Slack-only status behavior.
- added a fifth real shortcut/modal business closure:
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpensStatusModal|ViewSubmissionExecutesStatusCommand|ShortcutOpens(FindSkills|Settings|Model|Help|Status)Modal|ViewSubmissionExecutes(FindSkills|Settings|Model|Help|Status)Command)$'passed.
-
Continued WeChat consumer-validation slice:
- absorbed the runtime-create input-boundary fix so empty chat ids are rejected before any session/process side effects occur.
- kept the slice consumer-local inside
pkg/channels/wechat/control.goinstead of reopening shared binding contracts.
-
Verification run:
go test -count=1 ./pkg/channels/wechat -run 'TestControlServiceCreateRuntimeRejectsEmptyChatIDBeforeCreatingSession|TestRuntimeBindingService|TestControlService(DescribeBindings|CreateRuntime|DeleteRuntime|StopRuntime|RouteMessageToBoundRuntime)'passed.
-
Continued browser session dual-mode / reliability follow-up:
- absorbed the browser mode-preservation slice so internal navigate reuse in screenshot/execute_script now keeps the requested
mode. - absorbed the browser reliability slice so relative URLs are rejected before any browser startup/navigation attempt.
- kept the existing select-action correctness helper path (
buildSelectScript) in the same working state so the browser package remains self-consistent.
- absorbed the browser mode-preservation slice so internal navigate reuse in screenshot/execute_script now keeps the requested
-
Verification run:
go test -count=1 ./pkg/tools -run 'TestBrowserToolExecuteRejectsRelativeNavigateURL|TestBrowserToolGetTextRejectsRelativeURLBeforeNavigation|TestBrowserToolNavigationParamsPreserveMode|TestBrowserToolStartModeFromParams|TestBrowserToolExecuteRejectsInvalidMode|TestBrowserToolParametersIncludeRelayMode'passed.go test -count=1 ./pkg/toolspassed earlier in the same worktree state.
-
Continued conversation/thread binding slice 4 (target primary preservation on rebind):
- absorbed the contract-first shared-layer slice so rebinding a conversation onto a target session no longer steals that target session's existing primary conversation key.
- preserved the earlier deterministic source-session promotion behavior by updating the test ordering to reflect the new two-sided contract instead of the old single-sided assumption.
-
Verification run:
go test -count=1 ./pkg/conversationbindings -run 'TestService(RebindingPromotesDeterministicPrimaryConversation|RebindPreservesExistingPrimaryConversationOnTargetSession)$'passed.go test -count=1 ./pkg/conversationbindings ./pkg/channels/wechatpassed.
-
Continued channel capability matrix phase 5 (Telegram streaming scope):
- wired
pkg/channels/telegram/telegram.goto respect the declaredstreamingcapability before sending the thinking/streaming placeholder message. - kept the scope intentionally narrow: Telegram now preserves thinking messages in private chats and skips them in group/supergroup chats, matching the default
streaming=dmmatrix. - added regression coverage so group chats no longer get the streaming placeholder while private chats still do.
- wired
-
Verification run:
go test -count=1 ./pkg/channels/telegram -run 'TestSendThinkingMessageSkipsGroupsWhenStreamingUnsupported|TestSupportsInlineButtonsRespectsDefaultCapabilityScope|TestScopedInlineKeyboardDropsButtonsOutsideSupportedScope|TestSkillInstallPromptFallsBackToTextConfirmationWithoutInlineButtons'passed.go test -count=1 ./pkg/channels/telegrampassed.
-
Completed Slack interactive flow phase 5:
- added a fourth real shortcut/modal business closure:
helpshortcut now opens a modal and submission reuses the existing/helpcommand semantics. - kept the slice intentionally narrow by treating the modal as a thin UI shell over the existing command path instead of inventing Slack-only help behavior.
- added a fourth real shortcut/modal business closure:
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpensHelpModal|ViewSubmissionExecutesHelpCommand)$'passed.go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpens(FindSkills|Settings|Model|Help)Modal|ViewSubmissionExecutes(FindSkills|Settings|Model|Help)Command)$'passed.
-
Completed gateway control-plane hardening phase 17 (paired connection status count):
- extended
/api/v1/statuswithpaired_connectionsso the control plane can observe pairing volume without fetching the full connection list. - kept the slice read-only and derived from existing live connection state only.
- extended
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(StatusEndpoint|StatusEndpointReportsPairedConnections|StatusEndpointCountsConnectionsDeterministically)$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed gateway control-plane hardening phase 16 (pairing observability):
- extended gateway connection list/detail payloads with explicit
pairedandpaired_session_idfields. - kept the slice intentionally read-only: no changes to pairing protocol, ownership, or auth behavior.
- made unpaired live connections report
paired=falseinstead of relying on callers to infer it from missing session metadata.
- extended gateway connection list/detail payloads with explicit
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(ConnectionsEndpoint|GetConnectionEndpointReturnsConnectionDetails)$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed gateway control-plane hardening phase 15 (member self-managed delete):
- refined
DELETE /api/v1/connections/{id}so it no longer treats everymemberas globally forbidden. - kept
admin/ownersemantics unchanged while allowingmemberto delete only their own live gateway connection. - returned
404formemberattempts against other users' connections or nonexistent targets, so the endpoint does not leak connection existence.
- refined
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestDeleteConnectionEndpoint(AllowsMemberRoleForOwnedConnection|RejectsMemberRoleForOtherUsersConnection|ReturnsNotFoundForMemberWithoutOwnedTarget|RemovesClient|ReturnsNotFoundForUnknownClient|RequiresAuth)$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed channel capability matrix phase 2 (first runtime consumer):
- extracted the shared capability evaluation/model code into
pkg/channelcapabilitiesso channel runtime packages can consume the matrix without importingpkg/channelsand creating a cycle. - kept
pkg/channels/capabilities.goas a thin compatibility wrapper so existing callers and tests continue to use the same API surface. - wired
pkg/channels/whatsapp/whatsapp.goto respect the declarednative_commandscapability before routing slash commands, making WhatsApp's defaultnative_commands=offmatrix real at runtime. - added a regression test proving WhatsApp now forwards
/help ...as plain inbound text instead of executing the command handler when native commands are disabled.
- extracted the shared capability evaluation/model code into
-
Verification run:
go test -count=1 ./pkg/channels/whatsapp -run TestHandleInboundTreatsSlashCommandAsPlainTextWhenNativeCommandsDisabledfailed first, then passed after the fix.go test -count=1 ./pkg/channels/whatsapp ./pkg/channels -run 'Test(HandleInboundTreatsSlashCommandAsPlainTextWhenNativeCommandsDisabled|GetDefaultCapabilitiesForChannel|IsCapabilityEnabled|MergeCapabilities)$'passed.go test -count=1 ./pkg/channels/...passed.
-
Continued channel capability matrix phase 3 (Telegram inline button scope):
- wired
pkg/channels/telegram/telegram.goto evaluate the declaredinline_buttonscapability before attaching inline keyboards to the settings menu and skill-install confirmation flow. - kept the scope mapping intentionally narrow:
privatechats map todm;groupandsupergroupmap togroup. - made Telegram's default
inline_buttons=dmcapability real at runtime, so group/supergroup chats no longer receive those inline keyboards while private chats keep them. - added a text fallback for skill-install confirmation prompts when inline buttons are suppressed, so group chats still get a usable
/yes/noconfirmation path. - added regression tests for capability scope evaluation, keyboard suppression outside the supported scope, and the text fallback prompt.
- wired
-
Verification run:
go test -count=1 ./pkg/channels/telegram -run 'Test(SupportsInlineButtonsRespectsDefaultCapabilityScope|ScopedInlineKeyboardDropsButtonsOutsideSupportedScope|SkillInstallPromptFallsBackToTextConfirmationWithoutInlineButtons)$'failed first, then passed after the fix.go test -count=1 ./pkg/channels/telegrampassed.
-
Continued channel capability matrix phase 4 (WeWork native command scope):
- added an explicit default capability profile for
wework, withnative_commands=offinstead of falling back to the genericallbaseline. - wired
pkg/channels/wework/wework.goto respect the declared native-command capability before routing slash commands. - added runtime and matrix regression coverage so WeWork slash text now stays on the normal inbound bus path when native commands are disabled.
- added an explicit default capability profile for
-
Verification run:
go test -count=1 ./pkg/channels/wework ./pkg/channels -run 'Test(ProcessMessageTreatsSlashCommandAsPlainTextWhenNativeCommandsDisabled|GetDefaultCapabilitiesForChannel)$'failed first, then passed after the fix.go test -count=1 ./pkg/channels/wework ./pkg/channelspassed.
-
Completed browser session migration phase 3 (relay attach-only mode):
- added the first narrow
relaybrowser connection mode instead of continuing to reject it at the tool boundary. - kept the semantics intentionally strict: relay mode only attaches to an existing browser instance and never launches a new one, so the first slice is real but bounded.
- added regression coverage for relay mode parsing, relay start behavior with and without an existing instance, and invalid-mode rejection using a truly unsupported value.
- added the first narrow
-
Verification run:
go test -count=1 ./pkg/tools -run 'Test(ResolveBrowserMode|BrowserSessionStartWithModeRelay|BrowserToolStartModeFromParams|BrowserToolExecuteRejectsInvalidMode)$'passed.go test -count=1 ./pkg/toolspassed.
-
Completed conversation/thread binding slice 3 (deterministic rebinding promotion):
- added a rebinding regression test to prove that when one conversation moves to a different session, the old session promotes its next primary conversation deterministically instead of depending on metadata write order.
- updated the rebinding cleanup and persistence paths in
pkg/conversationbindings/service.goto reuse the same stable state ordering already used by binding queries. - verified the existing WeChat runtime consumer still works without adaptation after the rebinding contract tightened.
-
Verification run:
go test -count=1 ./pkg/conversationbindings -run 'TestServiceRebindingPromotesDeterministicPrimaryConversation$'passed.go test -count=1 ./pkg/conversationbindingspassed.go test -count=1 ./pkg/toolsessions ./pkg/conversationbindings ./pkg/channels/wechatpassed.
-
Completed gateway control-plane hardening phase 14 (pairing consistency hardening):
- allowed legacy gateway sessions with empty
sourcemetadata to be reused during websocket pairing, preserving old gateway session compatibility instead of rejecting them as non-gateway sessions. - rejected duplicate live attaches for the same paired
session_idand serialized the attach window so concurrent websocket upgrades cannot race into the same paired session. - enforced websocket inbound
session_idconsistency: when clients send a non-empty session id, it must match the active paired gateway session or the message is rejected before routing.
- allowed legacy gateway sessions with empty
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(WSChat(RejectsUnknownRequestedSessionBeforeUpgrade|UsesRequestedExistingGatewaySession|AllowsRequestedLegacyGatewaySessionWithEmptySource|RejectsSecondLiveConnectionForRequestedSession)|ProcessMessage(RejectsMismatchedInboundSessionID|AllowsMatchingInboundSessionID)|ProcessMessageUsesPairedSessionIDForRouterAndResponse)$'passed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed Slack interactive flow phase 4:
- added a third concrete shortcut/modal business closure:
modelshortcut now opens a modal and submission reuses the existing/modelcommand semantics. - kept the slice intentionally narrow by using the existing command path and returning the result as an ephemeral Slack response, instead of inventing Slack-only model inspection behavior.
- added regression coverage for model shortcut modal open and modal submission command execution, plus a combined regression pack covering all three real modal flows.
- added a third concrete shortcut/modal business closure:
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpensModelModal|ViewSubmissionExecutesModelCommand)$'passed.go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpens(FindSkills|Settings|Model)Modal|ViewSubmissionExecutes(FindSkills|Settings|Model)Command)$'passed.go test -count=1 ./pkg/channels/slack ./pkg/commandspassed.
-
Completed gateway control-plane hardening phase 13 (pairing handshake validation order):
- fixed the first pairing slice so invalid websocket
session_idvalues are rejected before websocket upgrade instead of after the connection has already been hijacked. - added a real websocket-dial regression test to prove clients now receive an actual HTTP
400for unknown requested sessions.
- fixed the first pairing slice so invalid websocket
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(WSChatRejectsUnknownRequestedSessionBeforeUpgrade|ResolveGatewaySessionIDRejectsUnknownRequestedSession)$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed gateway control-plane hardening phase 11 (member read-scope correction):
- corrected the previous endpoint-scope auth slice so
memberno longer reads global gateway status. - narrowed member access on
/api/v1/connectionsand/api/v1/connections/{id}to only the caller's ownuid-owned connections, while keeping delete operations restricted toadmin/owner.
- corrected the previous endpoint-scope auth slice so
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(Gateway(StatusEndpointRejectsMemberRole|ConnectionsEndpointAllowsMemberRoleForOwnedConnectionsOnly)|AuthenticateRequestAllowsMemberRoleForWebsocketPath|GetConnectionEndpoint(AllowsMemberRoleForOwnedConnection|RejectsMemberRoleForOtherUsersConnection))$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed gateway control-plane hardening phase 12 (websocket session pairing reuse):
- added a thin pairing path so
/ws/chat?session_id=<gateway-session>can reuse an existing gateway session instead of always minting a transient connection-scoped one. - rejected unknown
session_idvalues and non-gateway sessions with400, keeping the first pairing slice tightly bounded inside gateway-owned sessions only. - aligned router input and websocket reply
session_idvalues to the paired gateway session, so reconnect/reuse flows keep a stable session identity.
- added a thin pairing path so
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(ResolveGatewaySessionID(UsesRequestedExistingGatewaySession|RejectsUnknownRequestedSession|RejectsNonGatewaySession)|ProcessMessageUsesPairedSessionIDForRouterAndResponse)$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed gateway control-plane hardening phase 10 (endpoint-scoped REST auth):
- refined the previous role gate into explicit read/manage scopes in
pkg/gateway/server.goinstead of treating every REST control-plane endpoint as equally privileged. - allowed
membertokens to read/api/v1/status,/api/v1/connections, and/api/v1/connections/{id}while keepingDELETE /api/v1/connections/{id}restricted toadmin/owner. - preserved websocket chat compatibility by continuing to allow any valid authenticated token there, and preserved legacy tokens without a
roleclaim as admin-compatible.
- refined the previous role gate into explicit read/manage scopes in
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(Gateway(StatusEndpointAllowsMemberRole|ConnectionsEndpointAllowsMemberRole)|DeleteConnectionEndpointRejectsMemberRole|GetConnectionEndpointAllowsMemberRole|AuthenticateRequestAllowsMemberRoleForWebsocketPath)$'passed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Continued backlog execution from
task_plan.md:- re-read the persistent planning artifacts and GoalX run state.
- confirmed the previous GoalX run is complete, while the next approved mainline remains
gateway control-plane hardening. - narrowed the next minimal slice to
gateway.allowed_ipsso gateway gets a shared IP allowlist across websocket handshake and REST control-plane entrypoints.
-
Completed gateway control-plane hardening phase 7 (IP allowlist):
- added
gateway.allowed_ipstopkg/config.GatewayConfigand kept it runtime-reloadable. - validated
allowed_ipsentries so blanks and non-IP literals fail fast in config validation instead of deferring to runtime surprises. - added a shared
checkClientIP()gate inpkg/gateway/server.goand applied it to both websocket handshake and REST control-plane entrypoints. - kept the first version intentionally narrow: exact literal IP matching only, no CIDR, no forwarded-header trust chain, no pairing/scope semantics.
- added
-
Verification run:
go test -count=1 ./pkg/gateway ./pkg/config -run 'Test(Gateway(CheckClientIP(AllowsRequestsWhenListUnset|AllowsConfiguredIP|RejectsUnconfiguredIP)|StatusEndpoint(RejectsDisallowedIP|AllowsConfiguredIP)|WSChatRejectsDisallowedIP)|ValidatorRejects(BlankGatewayAllowedIPs|InvalidGatewayAllowedIPs))$'passed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed gateway control-plane hardening phase 8 (per-IP rate limit):
- added
gateway.rate_limit_per_minutetopkg/config.GatewayConfigand kept it runtime-reloadable. - validated negative
rate_limit_per_minutevalues as config errors. - added shared per-IP limiter state in
pkg/gateway/server.goand enforced it at both REST control-plane and websocket handshake entrypoints. - kept the first version intentionally narrow: remote-IP buckets only, no session/user scope, no pairing integration, no advanced eviction policy.
- added
-
Verification run:
go test -count=1 ./pkg/gateway ./pkg/config -run 'Test(Gateway(RateLimit(AllowsRequestsWhenUnset|RejectsSecondRequestFromSameIP|UsesPerIPBuckets)|StatusEndpointRejectsRateLimitedRequest|WSChatRejectsRateLimitedRequest)|ValidatorRejectsNegativeGatewayRateLimitPerMinute)$'passed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed gateway control-plane hardening phase 9 (control-plane auth scope):
- added a shared gateway JWT parser in
pkg/gateway/server.goso gateway can consistently readsub/uid/rolefrom existing WebUI-issued tokens. - tightened REST control-plane authorization so
/api/v1/statusand connection-management endpoints now requireadmin/owner, instead of accepting any valid JWT. - preserved websocket chat compatibility by continuing to allow any valid authenticated token there, and preserved legacy control-plane tokens by treating missing
roleclaims as admin-compatible.
- added a shared gateway JWT parser in
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestGateway(StatusEndpointRejectsMemberRole|ConnectionsEndpointRejectsMemberRole|AuthenticateRequestAllowsMemberRoleForWebsocketPath)$'passed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed Slack interactive flow phase 3:
- added a second real shortcut/modal business closure on top of the existing Slack interactive routing skeleton:
settingsshortcut now opens a modal and submission reuses the existing/settingscommand semantics. - kept the slice intentionally narrow by treating the modal as a thin UI shell over the already-existing settings command, instead of inventing a parallel Slack-only settings protocol.
- added regression coverage for settings shortcut modal open and modal submission command execution.
- added a second real shortcut/modal business closure on top of the existing Slack interactive routing skeleton:
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpens(Settings|FindSkills)Modal|ViewSubmissionExecutes(Settings|FindSkills)Command)$'passed.go test -count=1 ./pkg/channels/slack ./pkg/commandspassed.
-
Completed context economy preflight execution telemetry slice:
- added
preflight.appliedto the shared preflight decision shape so route metadata can distinguish "recommended" from "actually executed". - updated both legacy and blades orchestrators to mark
applied=trueonly whencompact_before_runreally executes, while warning-only paths stayfalse. - extended websocket route payloads and Chat UI rendering so the frontend can show when preflight compaction was actually applied.
- added
-
Verification run:
go test -count=1 ./pkg/agent -run 'TestChatWithPromptContextDetailed_(IncludesContextPressurePreview|DoesNotAutoCompressWarningPreflightBeforeModelCall|AutoCompressesCriticalPreflightBeforeBlades)$'passed.go test -count=1 ./pkg/webui -run '^TestChatRouteStateJSONIncludesContextPressureFields$'passed.go test -count=1 ./pkg/agent ./pkg/webuipassed.
-
Completed gateway control-plane hardening phase 2 (REST auth gate):
- added a shared
requireAuthenticatedAPI()gate inpkg/gateway/server.go. - changed
GET /api/v1/statusandGET /api/v1/connectionsto require the same JWT authentication path already used by websocket clients instead of exposing control-plane data anonymously. - added regression coverage in
pkg/gateway/server_test.gofor both unauthorized and authorized REST access paths.
- added a shared
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(Gateway(StatusEndpointRequiresAuth|ConnectionsEndpointRequiresAuth)|StatusEndpoint|ConnectionsEndpoint)$'failed first, then passed after the fix.go test -count=1 ./pkg/gatewaypassed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed gateway control-plane hardening phase 3 (connection delete endpoint):
- added
DELETE /api/v1/connections/{id}inpkg/gateway/server.goso the control plane can actively terminate a single websocket client connection. - kept the endpoint behind the same JWT auth gate used by the other control-plane REST endpoints.
- added regression coverage in
pkg/gateway/server_test.gofor successful removal, unknown-client404, and unauthenticated401.
- added
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestDeleteConnectionEndpoint(RemovesClient|ReturnsNotFoundForUnknownClient|RequiresAuth)$'failed first, then passed after the fix.go test -count=1 ./pkg/gatewaypassed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed gateway control-plane hardening phase 4 (stable connection list metadata):
- extended gateway clients with connection metadata capture for
connected_atandremote_addr. - changed
GET /api/v1/connectionsto return a stable, sorted connection list withsession_id,connected_at, andremote_addrfields instead of depending on Go map iteration order. - added regression coverage in
pkg/gateway/server_test.gofor sorted connection output and status reporting with active connections.
- extended gateway clients with connection metadata capture for
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(ConnectionsEndpoint|StatusEndpointCountsConnectionsDeterministically)$'failed first, then passed after the fix.go test -count=1 ./pkg/gatewaypassed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed gateway control-plane hardening phase 5 (connection detail endpoint):
- added
GET /api/v1/connections/{id}inpkg/gateway/server.goso the control plane can inspect a single websocket connection without fetching the full list. - reused the same JWT auth gate and shared connection serialization shape as the list endpoint.
- added regression coverage in
pkg/gateway/server_test.gofor success, unknown-client404, and unauthenticated401.
- added
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestGetConnectionEndpoint(ReturnsConnectionDetails|ReturnsNotFoundForUnknownClient|RequiresAuth)$'failed first, then passed after the fix.go test -count=1 ./pkg/gatewaypassed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed gateway control-plane hardening phase 6 (max connection limit):
- added
gateway.max_connectionstopkg/config.GatewayConfigwith0meaning unlimited. - validated negative
max_connectionsvalues as config errors. - added a server-side
checkConnectionLimit()guard so websocket upgrades are rejected once the configured limit is reached. - added regression coverage in
pkg/gateway/server_test.goandpkg/config/path_test.gofor limit enforcement and config validation.
- added
-
Verification run:
go test -count=1 ./pkg/gateway ./pkg/config -run 'Test(Gateway(RejectsConnectionsAboveConfiguredLimit|AllowsConnectionsWhenLimitUnset)|ValidatorRejectsNegativeGatewayMaxConnections)$'failed first, then passed after the fix.go test -count=1 ./pkg/gatewaypassed.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed conversation/thread binding slice 2 (deterministic query order):
- added
TestServiceBindingQueriesReturnDeterministicConversationOrderinpkg/conversationbindings/service_test.goto lock a real missing contract with TDD. - confirmed RED first:
ListBindings()returned[chat-b chat-a]when bindings were created in non-sorted order. - updated
pkg/conversationbindings/service.gososessionToBindingRecords,ListBindings, andGetBindingsBySessionnow return a stable order byconversation_idinstead of leaking metadata write order. - verified the existing WeChat runtime consumer remained compatible without code changes.
- added
-
Verification run:
go test -count=1 ./pkg/conversationbindings -run 'TestServiceBindingQueriesReturnDeterministicConversationOrder'failed first, then passed after the fix.go test -count=1 ./pkg/conversationbindingspassed.go test -count=1 ./pkg/toolsessions ./pkg/conversationbindings ./pkg/channels/wechatpassed.
-
Completed Slack interactive flow phase 2:
- extended the Slack API abstraction with
OpenView(...)so shortcut handlers can open modals through the existing channel abstraction. - implemented the first real shortcut/modal business flow:
find_skillsshortcut now opens a modal, andfind_skills_modalsubmission re-runs the existingfind-skillscommand with the submitted query. - kept the flow narrow by reusing the existing skill-install confirmation message path instead of inventing a second install-confirm mechanism for modals.
- added regression coverage in
pkg/channels/slack/slack_test.gofor shortcut modal open and view-submission command execution.
- extended the Slack API abstraction with
-
Verification run:
go test -count=1 ./pkg/channels/slack -run 'TestHandle(ShortcutOpensFindSkillsModal|ViewSubmissionExecutesFindSkillsCommand)'failed first, then passed after the fix.go test -count=1 ./pkg/channels/slackpassed.go test -count=1 ./pkg/channels/slack ./pkg/commandspassed.
-
Completed gateway control-plane hardening phase 1 (origin allowlist):
- added
gateway.allowed_originstopkg/config.GatewayConfig. - replaced the fully-open websocket
CheckOriginbehavior with a server-level allowlist check. - kept requests without an
Originheader allowed so existing non-browser clients still work. - added regression coverage in
pkg/gateway/server_test.gofor allowed and blocked origins, and updatedpkg/config/path_test.gofor the new config field.
- added
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestGatewayCheckOrigin(AllowsConfiguredOrigins|AllowsRequestsWithoutOrigin)'failed first, then passed after the fix.go test -count=1 ./pkg/gateway ./pkg/configpassed.
-
Completed channel capability matrix phase 1:
- added
pkg/channels/capabilities.goto import the coregoclawcapability model: capability types, scope parsing, enablement checks, merge behavior, and per-channel default matrices. - kept this slice intentionally low risk by landing the shared declaration/evaluation layer first without forcing all channel runtimes to consume it in the same commit.
- added regression coverage in
pkg/channels/capabilities_test.gofor default matrices, scope-based enablement, and override merge behavior.
- added
-
Verification run:
go test -count=1 ./pkg/channels -run 'GetDefaultCapabilitiesForChannel|IsCapabilityEnabled|MergeCapabilities'passed.go test -count=1 ./pkg/channelspassed.
-
Completed browser advanced extraction phase 5 (
get_text):- extended
pkg/tools/browser.goso thebrowsertool now exposes aget_textaction for plain-text extraction on top of the existingget_htmlpath. - imported the lightweight
goclawHTML-to-text slice as a local helper instead of adding a new browser tool type or extra dependency surface. - added regression coverage in
pkg/tools/browser_test.gofor action exposure and HTML tag stripping.
- extended
-
Verification run:
go test -count=1 ./pkg/tools -run 'BrowserToolParametersIncludeGetText|HTMLToTextStripsTags'passed.go test -count=1 ./pkg/toolspassed.
-
Completed browser advanced extraction phase 4 (
extract_structured_data):- extended
pkg/tools/browser.goso thebrowsertool now exposes anextract_structured_dataaction withextract_typemodes forall,schema_org,json_ld, andmeta. - imported the
goclawstructured-data extraction slice by adding reusable extraction-script builders and CDP result formatting directly into the existingBrowserTool. - added regression coverage in
pkg/tools/browser_test.gofor action exposure and extractor script composition.
- extended
-
Verification run:
go test -count=1 ./pkg/tools -run 'BrowserTool(ParametersIncludeExtractStructuredData|BuildExtractionScript)'passed.go test -count=1 ./pkg/toolspassed.
-
Completed browser advanced extraction phase 3 (
print_pdf):- extended
pkg/tools/browser.goso thebrowsertool now exposes aprint_pdfaction plus PDF-specific parameters for orientation, header/footer, background rendering, and margins. - imported the
goclawBrowserPrintToPDFslice in anekobot-native form by reusing the existing session manager and output directory instead of adding a separate CDP tool type. - added regression coverage in
pkg/tools/browser_test.goforprint_pdfaction exposure and PDF argument construction defaults/overrides.
- extended
-
Verification run:
go test -count=1 ./pkg/tools -run 'BrowserTool(ParametersIncludePrintPDF|BuildPrintToPDFArgs)'passed.go test -count=1 ./pkg/toolspassed.
-
Completed MaixCAM outbound response routing:
- re-audited
pkg/channels/maixcam/maixcam.goand found slash-command responses were already written back to the active device connection, so the real remaining gap was outbound bus replies being broadcast to every connected device. - updated
SendMessageto parsemaixcam:<device>session IDs and route outbound replies only to the targeted device connection while preserving broadcast behavior when no device target is present. - added regression coverage in
pkg/channels/maixcam/maixcam_test.gofor device-targeted send, broadcast fallback, and session parsing.
- re-audited
-
Verification run:
go test -count=1 ./pkg/channels/maixcam -run 'SendMessage(TargetsSessionDevice|BroadcastsWithoutTargetSession)|MaixcamDeviceIDFromSession'failed first, then passed after the fix.go test -count=1 ./pkg/channels/maixcampassed.
-
Completed memory quality pack phase 4 (embedding cache):
- added
pkg/memory/embedding_cache.goto import the usefulgoclawLRU cache idea in anekobot-appropriate form: caching embedding vectors by input text instead of redundantly caching objects already held by the in-memory store. - updated
pkg/memory/manager.goso bothAddandSearchreuse cached embedding vectors for repeated text, reducing duplicate provider calls while keeping the store and search interface unchanged. - added regression coverage in
pkg/memory/manager_cache_test.goto prove repeatedAddand repeatedSearchon the same text only invoke the embedding provider once.
- added
-
Verification run:
go test -count=1 ./pkg/memory -run 'Manager(SearchCachesQueryEmbeddings|AddCachesEmbeddingsForRepeatedText)'failed first, then passed after the fix.go test -count=1 ./pkg/memorypassed.
-
Completed browser session migration phase 2:
- extended
pkg/tools/browser.goso thebrowsertool schema now exposes amodeparameter withauto/directoptions instead of hiding session startup strategy inside tool internals. - wired
navigateto pass the resolved startup mode intoBrowserSession.StartWithMode, so callers can explicitly request direct attach semantics while keeping auto-mode reuse as the default. - added regression coverage in
pkg/tools/browser_test.gofor default/direct mode parsing and explicit rejection of unsupported modes likerelaybefore any browser startup happens.
- extended
-
Verification run:
go test -count=1 ./pkg/tools -run 'BrowserToolStartMode|BrowserToolExecuteRejectsInvalidMode|BrowserSession|ResolveBrowserMode'passed.go test -count=1 ./pkg/toolspassed.
-
Completed browser session migration phase 1:
- extended
pkg/tools/browser_session.gowith explicitauto/directconnection modes instead of only a fixed single-path startup flow. - added a reuse-first strategy so browser sessions now try to attach to existing Chrome debug ports before falling back to launching a new headless instance.
- added regression coverage in
pkg/tools/browser_session_test.gofor mode parsing, auto-mode fallback-to-launch, and direct-mode reuse of an existing browser instance.
- extended
-
Verification run:
go test -count=1 ./pkg/tools -run 'BrowserSession|ResolveBrowserMode'passed.go test -count=1 ./pkg/toolspassed.
-
Completed memory quality pack phase 3 (temporal decay):
- added
pkg/memory/temporal_decay.goto import the coregoclawtime-aware ranking slice for builtin memory search. - extended
pkg/memory/types.gowithTemporalDecayConfigandSearchOptions.TemporalDecay, then applied temporal decay insidepkg/memory/manager.gobefore MMR so age-adjusted scores feed later diversity re-ranking. - added regression coverage in
pkg/memory/search_manager_test.gofor pure decay ordering and manager-level search behavior with temporal decay enabled.
- added
-
Verification run:
go test -count=1 ./pkg/memorypassed.
-
Completed memory quality pack phase 2 (MMR):
- added
pkg/memory/mmr.goto import the coregoclawMMR re-ranking slice for builtin memory search. - extended
pkg/memory/types.gowithMMRConfigandSearchOptions.MMR, then applied MMR insidepkg/memory/manager.goafter raw store search so diversity re-ranking stays isolated from storage code. - added regression coverage in
pkg/memory/search_manager_test.gofor direct MMR ordering and manager-level search behavior with MMR enabled.
- added
-
Verification run:
go test -count=1 ./pkg/memorypassed.
-
Completed memory quality pack phase 1 (citations):
- added
pkg/memory/citations.goto import the useful citation-formatting slice fromgoclawin a way that fitsnekobot's existing memory types. - extended
pkg/memory/types.gowithEndLineNumber,Timestamp, and result-levelCitation/AgeInDaysfields so later memory-quality slices have a compatible shape. - updated
pkg/memory/manager.goandpkg/tools/memory.goso both direct memory context rendering and the memory tool render unified citation strings likepath#Lx-Lyinstead of bare file paths. - added regression coverage in
pkg/memory/search_manager_test.goandpkg/tools/memory_test.gofor citation decoration and display formatting.
- added
-
Verification run:
go test -count=1 ./pkg/memory ./pkg/toolspassed.
-
Completed conversation/thread binding migration phase 1:
- extended
pkg/conversationbindings/service.gofrom a thin bind/resolve wrapper into a reusable binding layer withBindWithOptions, richBindingRecordviews,GetBinding,ListBindings,GetBindingsBySession, andCleanupExpired. - kept persistence on top of existing tool-session records to avoid schema churn while still importing the useful
goclawideas: binding metadata, target kind/placement, conversation view, and expiry cleanup. - tightened
Listbehavior so the service only returns sessions that actually match the configured channel + prefix instead of every session from the same source. - added regression coverage for filtered listing, metadata-bearing binds, session-based lookup, and expired-binding cleanup; verified WeChat runtime binding tests remain green.
- extended
-
Verification run:
go test -count=1 ./pkg/conversationbindingspassed.go test -count=1 ./pkg/channels/wechatpassed.go test -count=1 ./pkg/toolsessionspassed.
-
Completed Tool Sessions / QMD runtime admin smoke pack:
- added
pkg/webui/server_toolsessions_test.goas a dedicated WebUI regression pack for tool sessions. - covered owner isolation, attach-token create/consume flow, OTP generation + access login, one-time password consumption, process status/input/output/kill flow, terminated-session archival, and tool-event cleanup.
- re-used existing QMD handler coverage in
pkg/webui/server_status_test.goas the backend smoke baseline for status/update/install/cleanup behavior, so Batch C now has both prompts and runtime-admin smoke coverage recorded.
- added
-
Verification run:
go test -count=1 ./pkg/webui -run 'ToolSession|QMD|Status|Session'passed.go test -count=1 ./...passed.npm --prefix pkg/webui/frontend run buildcould not run in the current shell becausenpmis missing;pnpmis present on disk but also fails becausenodeis not available onPATH.
-
Completed Runtime Prompts regression pack and checklist:
- added
pkg/prompts/manager_test.goto cover scope override semantics, disabled prompt/binding filtering, session binding replacement, and render-context separation betweensystem_textanduser_text. - found and fixed a real bug in
pkg/prompts/manager.go: when the same prompt was bound in multiple scopes,Resolvepreviously let earlier query order win, soglobalcould incorrectly overridechannelorsession; resolution now explicitly prefers narrower scope, then lower priority. - added WebUI regression coverage in
pkg/webui/server_prompts_test.gofor scope override plus render-context fields (channel,route,workspace,custom). - added
docs/RUNTIME_PROMPTS.mdwith behavior notes and a reusable smoke checklist, and linked it fromREADME.md.
- added
-
Verification run:
go test -count=1 ./pkg/promptspassed.GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/prompts ./pkg/webuipassed.
-
Completed Slack interactive flow phase 1:
- added Slack-side pending interaction state for skill install confirmations, aligned with Telegram/Discord semantics.
- changed Slack slash-command skill install confirmation from “send a pseudo inbound message” to “store pending interaction, require same-user confirm/cancel, expire after 15 minutes, re-run the original command with confirmation metadata, and update the original Slack message with the result”.
- introduced a narrow Slack API interface to make the channel logic testable without live Slack I/O.
- added placeholder shortcut / view-submission routing hooks so later modal/shortcut flows have a stable entry point instead of being hard-coded into the event switch.
- added regression tests for pending-state storage, confirm execution path, cancel path, and expiry cleanup in
pkg/channels/slack/slack_test.go.
-
Verification run:
go test -count=1 ./pkg/channels/slackpassed.GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/channels/slack ./pkg/commandspassed.
-
Re-baselined the post-WeChat migration plan:
- confirmed the latest WeChat migration commits are already pushed to
origin/main. - updated
task_plan.mdto mark the WeChat workstream as stage-complete for now and narrowed the next execution target to Slack interactive flow completion. - rewrote the Slack backlog item to reflect the real gap: missing pending state, expiry cleanup, message update path, and extensible shortcut/modal routing rather than only “callback exists / not exists”.
- confirmed the latest WeChat migration commits are already pushed to
-
Added WeChat presenter-style output guidance for agent turns:
- prepended WeChat-specific output rules before user messages so the agent is explicitly told to avoid Markdown and prefer local absolute file paths for rich content.
- included workspace-root hints in the injected WeChat instructions so generated attachment files have a stable preferred location.
- added regression tests for presenter prompt assembly.
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/channels/wechat ./pkg/wechat/... ./pkg/webuipassed.
-
Added the first WeChat weak-interaction slice:
- wired command responses with
commands.InteractionTypeSkillInstallConfirminto the WeChat channel. - added pending interaction state per WeChat user and command-style confirmation handling for
/yes,/no, and/cancel. - aligned the confirmation execution path with Telegram/Discord by re-running the command with
__confirm_install__ <repo>andskill_install_confirmed_repometadata. - added regression tests for action parsing, pending interaction expiry, confirm execution, deny handling, and prompt rendering.
- wired command responses with
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/channels/wechat ./pkg/wechat/... ./pkg/webuipassed.
-
Removed the obsolete channel-local WeChat protocol layer:
- deleted
pkg/channels/wechat/protocol.goafter confirming channel runtime, send path, and WebUI QR bind flow all use sharedpkg/wechatpackages. - simplified
pkg/channels/wechat/channel.goto keep only bot-backed channel glue instead of duplicated client/credential protocol state.
- deleted
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/channels/wechat ./pkg/wechat/... ./pkg/webuipassed.
-
Completed WeChat channel shared-SDK migration and attachment send pipeline:
- switched
pkg/channels/wechatruntime monitor, typing keepalive, outbound text/image/file/video sending, and QR binding helpers to sharedpkg/wechatSDK primitives. - replaced rendered markdown image sending from channel-local inline payloads with shared uploader-based image sending.
- added outbound file-path extraction/cleanup so reply text can promote local absolute paths into WeChat image/video/file attachments while removing those paths from the final text body.
- aligned credential storage with shared
pkg/wechat/types.Credentials. - added regression tests for file-path extraction and attachment classification in
pkg/channels/wechat/attachments_test.go.
- switched
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/channels/wechat ./pkg/wechat/... ./pkg/webuipassed.
-
Completed
gua/libc/wechatSDK baseline migration intonekobot/pkg/wechat:- added shared
types / client / auth / cdn / messaging / monitor / parse / typing / voice / botpackages underpkg/wechat. - kept existing
pkg/channels/wechatworking while introducing the new shared SDK layer, so follow-up channel enhancements can build on stable primitives instead of channel-local protocol code.
- added shared
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/wechat/... ./pkg/channels/wechatpassed.
-
Re-ordered the WeChat workstream per latest requirement:
- promoted
gua/libc/wechatSDK full migration intonekobot/pkg/wechatas the current feature slice. - moved WeChat attachment/file-path send-path enhancement behind the shared SDK migration.
- promoted
-
Re-scoped the next channel migration slice to WeChat SDK/send-path improvements:
- switched reference source from
goclawtoguafor WeChat-specific presenter / formatter / upload behavior. - identified the highest-value low-risk gap in
nekobot: outbound replies cannot yet turn local file paths into WeChat image/video/file attachments. - updated
task_plan.mdto prioritize a WeChat attachment send pipeline before broader Slack interaction work.
- switched reference source from
-
Implemented subagent completion notification flow and spawn context propagation:
- Added
pkg/subagentnotification payload + outbound sender abstraction so finished tasks can render origin-channel notifications without coupling the package to the bus implementation. - Wired agent startup to enable subagents and bridge notifications into the message bus outbound path.
- Registered the
spawntool in agent runtime and propagated channel/session route context into spawn tool execution. - Updated direct channel agent call sites (Telegram / ServerChan / WeChat) to use
ChatWithPromptContextso tool execution has origin channel/session metadata.
- Added
-
Added regression tests for:
- subagent notification rendering/sending (
pkg/subagent/notify_test.go) - spawn context route propagation (
pkg/tools/spawn_test.go) - agent spawn tool registration (
pkg/agent/agent_test.go)
- subagent notification rendering/sending (
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./pkg/subagent ./pkg/tools ./pkg/agentpassed.GOPROXY=https://goproxy.cn,direct go test -count=1 ./...passed.
-
Updated planning artifacts after completing this feature:
- marked
Subagent 完成通知真正回推 origin channelcomplete intask_plan.md - marked Batch B
Subagent origin notify 接线complete
- marked
-
Re-audited
nekobotagainst current code,~/code/goclaw, and~/code/gua, then rewrote the task backlog to distinguish completed baseline vs actual remaining gaps. -
Cleared stale backlog items that are already implemented:
/gateway restartand/gateway reloadare implemented inpkg/gateway/controller.go.- memory hybrid text similarity already exists in
pkg/memory/store.go. - skills version/tool comparison already exists in
pkg/skills/eligibility.go. - cron
at/every/delete_after_run/run-nowalready exist inpkg/cron/*, WebUI, and CLI.
-
Confirmed current stable baseline now includes:
- Web-first runtime admin for prompts / tool sessions / QMD / skills runtime status
- provider fallback + cooldown + route override
- session history sanitize / safe history / context compression
- multi-path skills with snapshots/versioning
-
Added new migration backlog sourced from
goclaw:- general thread/conversation binding layer
- memory quality pack (MMR / temporal decay / citations / cache)
- gateway control-plane hardening
- browser dual-mode session and advanced extraction
- OAuth credential manager
-
Added new migration backlog sourced from
gua:- user-scoped external agent runtime foundation
- permission / elicitation bridge for external agents
- WeChat presenter and attachment-output pipeline
- runtime prompt detection / tmux-style interactive control
- channel interaction model for weak-interaction platforms
-
Updated
task_plan.mdto reflect:- completed capabilities
- real unfinished gaps
- new Batch A-E execution order
- rule that each completed feature must be committed and pushed individually
-
Added runtime-backed prompt management with Ent schemas for
promptandprompt_binding, including CRUD, binding resolution, and render helpers inpkg/prompts. -
Wired prompt manager into FX/runtime startup and exposed WebUI prompt APIs with server-side tests in
pkg/webui/server_prompts_test.go. -
Added frontend Prompts page and
usePromptshook, plus supporting textarea component and i18n entries. -
Expanded runtime admin flows around tool sessions, config, providers, marketplace, QMD inspection, and status endpoints to support the web-first dashboard model.
-
Added workspace-aware QMD path resolution and improved session export defaults/visibility, including resolved export directory and cleanup support.
-
Improved skills runtime metadata handling with snapshot/version coverage and added regression tests for snapshot/version behavior.
-
Added provider cooldown tests and related runtime integration updates.
-
Updated README and QMD docs to reflect the current Web-first setup and Docker/QMD behavior.
-
Created and pushed commit
58877a5(feat(runtime): add web-managed prompts and tool session controls). -
Follow-up needed on next device/session:
- Run
go test -count=1 ./... - Run
npm --prefix pkg/webui/frontend run build - Manually smoke test Prompts page, tool session controls, and QMD admin flow in WebUI
- Run
- Initialized planning artifacts for provider DB migration task.
- Inspected current provider backend/frontend implementation paths.
- Confirmed provider CRUD currently depends on config file persistence and draft-based frontend flow.
- Next: implement DB-backed provider store and wire into WebUI handlers.
- Implemented
pkg/providerstorewith SQLite provider CRUD and runtime config sync. - Wired provider store into WebUI APIs and gateway/CLI startup module graphs.
- Updated dashboard provider dialog: clicking dialog Apply now directly persists provider changes.
- Adjusted storage to reuse the existing single DB file
tool_sessions.dbper user request. - Verification:
go test ./...passed. - Refactored Ent location to a single shared path: moved generated code from
pkg/toolsessions/enttopkg/storage/entand updated all imports. - Re-ran verification after Ent refactor:
go test ./...passed. - Implemented runtime config DB store (
config_sections) on sharedtool_sessions.dband startup overlay logic. - Migrated WebUI save paths for init password, channel updates, global config save, and chat routing persistence from file writes to DB writes.
- Added config DB store tests and verified with
go test ./.... - Updated
config/config.example.json,pkg/config/config.example.json, anddocs/CONFIG.mdto match latest minimal-file + DB-runtime behavior. - Replanned config UX: switched dashboard Config from whole-document JSON editing to section-scoped editing with section selector/reset/save.
- Removed outdated provider-in-config snippets from README/docs and aligned examples to bootstrap-only config.
- Added WebUI config section into
/api/configread/write path and persisted via DB section storage. - Validation run:
go test ./...passed.
- Completed memory storage abstraction hardening with
MemoryBackendimplementations for file/db/kv/noop. - Fixed file backend I/O in
pkg/agent/memory_backend.goto useos.MkdirAll+os.ReadFile+os.IsNotExistwhile keeping atomic writes. - Fixed
NewMemoryStorefallback typing inpkg/agent/memory.goto safely degrade to noop backend when file backend init fails. - Added
pkg/agent/memory_backend_test.goto verify KV backend selection, DB backend selection, and KV-unavailable fallback to file backend. - Verification run:
go test ./pkg/agent ./pkg/config ./cmd/nekobotpassed. - Added ACP stdio entrypoint command
nekobot acpwith FX wiring and lifecycle management incmd/nekobot/acp.go. - Extended ACP session state and adapter mapping so
session/newmcpServersare converted intoconfig.MCPServerConfigand stored per ACP session. - Updated blades orchestrator tool resolver path to honor ACP session-level MCP overrides, while keeping existing provider fallback and tool execution flow unchanged.
- Added MCP transport compatibility for ACP
sseby mapping to blades HTTP transport and expanded config validation to acceptssetransport values. - Added ACP adapter tests for session creation/mode/cancel/prompt validation and MCP mapping coverage in
pkg/agent/acp_adapter_test.go. - Added ACP
session/updatebridge in adapterPromptflow to emit agent text chunks via ACP connection while preserving existing provider fallback and tool execution semantics. - Wired ACP adapter to
AgentSideConnectionin CLI startup so session update notifications are available in real ACP runtime. - Expanded ACP adapter tests to cover session update emission, session update failure/cancel handling, and connection-detach behavior.
- Verification run:
go test ./pkg/agent ./pkg/config ./cmd/nekobotpassed. - Added ACP
session/loadsupport in adapter with absolute CWD validation, in-memory session bootstrap, and per-session MCP override mapping. - Updated ACP initialize capability to advertise
loadSession=trueso ACP clients can restore existing session IDs. - Added ACP adapter tests for
InitializeloadSession capability plusLoadSessionsuccess and validation failure paths. - Added ACP session model state exposure in
session/newandsession/loadresponses to reflect the session’s active model. - Implemented ACP experimental
session/set_modelhandling with per-session model override updates and validation. - Expanded ACP adapter tests to cover loaded/new session model state plus
session/set_modelsuccess and invalid-param cases. - Verification run:
go test -count=1 ./pkg/agent ./pkg/config ./cmd/nekobotpassed. - Added ACP
current_mode_updatesession notifications insession/set_modeso clients receive mode-change updates immediately. - Added ACP adapter tests for
session/set_modenotification emission plus update failure/cancel handling. - Verification run:
go test -count=1 ./pkg/agent ./pkg/config ./cmd/nekobotpassed. - Added trailing user-message dedup normalization in
pkg/agent/context.go(BuildMessages) to avoid double-injecting the current prompt when callers pre-append user turns. - Applied the same trailing user-message normalization in
pkg/agent/blades_runtime.gobefore hydrating blades session history, keeping blades runtime behavior aligned with legacy prompt construction semantics. - Added regression tests in
pkg/agent/agent_test.gofor trailing-current-user dedup and non-matching history preservation. - Verification run:
go test -count=1 ./pkg/agent ./pkg/gateway ./pkg/webui ./cmd/nekobot ./pkg/configpassed. - Note: full
go test -count=1 ./...still fails inpkg/cronwith known upstreamfatal error: concurrent map writesin Ent atlas migration path (unchanged by this batch). - Fixed runtime Ent schema migration race by serializing
EnsureRuntimeEntSchemacalls with a package-level mutex inpkg/config/runtime_client.go. - Added regression test
TestEnsureRuntimeEntSchemaConcurrentCallsinpkg/config/db_store_test.goto verify concurrent schema init no longer fails. - Verification run:
go test -count=1 ./pkg/config ./pkg/cronpassed. - Verification run:
go test -count=1 ./...passed. - Added deterministic tool description ordering in
pkg/agent/context.goby sorting tool descriptions before assembling the tools section, improving prompt cache stability. - Added regression test
TestBuildToolsSection_SortsToolDescriptionsDeterministicallyinpkg/agent/agent_test.go. - Verification run:
go test -count=1 ./pkg/agentpassed. - Verification run:
go test -count=1 ./...passed. - Wired
Agent.callLLMWithFallbackto provider failover semantics (providers.ClassifyError+ sharedCooldownTracker) so retriable failures continue fallback and non-retriable format errors fail fast. - Added provider cooldown skip behavior in agent fallback path, including contextual logging for skip reason and remaining cooldown window.
- Added agent failover regression tests for retriable fallback continuation, non-retriable short-circuit behavior, and cooldown-based skip on subsequent attempts (
pkg/agent/agent_test.go). - Added provider failover/cooldown regression tests covering cooldown skipping, non-retriable stop, reason tracking, all-cooldown exhaustion, and 24h failure-window reset (
pkg/providers/loadbalancer_test.go). - Added
alwaysfrontmatter support in skills metadata (pkg/skills/manager.go) with eligibility-aware always-skill selection. - Updated skill prompt assembly to emit a dedicated
Always SkillsXML section and keep regular skills in deterministic name order. - Added compatibility parsing for
metadata.openclaw.alwaysinpkg/skills/loader.go. - Extended validation with
ValidateAlwaysto warn when always-on skills are disabled. - Added regression tests for always-skill loading, prompt rendering, and validation (
pkg/skills/manager_test.go,pkg/skills/loader_test.go). - Updated docs with
alwaysfield and Always Skills behavior (docs/CONFIG.md). - Verification run:
go test -count=1 ./pkg/skills ./pkg/agentpassed. - Continued Skills follow-up: switched
# Available Skillsfrom full markdown bodies to compact XML summary (<skills><skill ... /></skills>) inpkg/skills/manager.go, keeping Always Skills full XML instructions unchanged. - Added
instructions_lengthsummary field using rune count for deterministic lightweight metadata and better token budgeting hints. - Added/updated regression tests for compact summary output and non-ASCII length handling in
pkg/skills/manager_test.go. - Verification run:
go test -count=1 ./pkg/skills ./pkg/agentpassed. - Verification run:
go test -count=1 ./...passed. - Added WebUI Cron API routes and handlers in
pkg/webui/server.gofor list/create/delete/enable/disable/run-now operations. - Added structured error logging for Cron mutation failures in WebUI handlers (delete/enable/disable/run).
- Added WebUI Cron handler tests in
pkg/webui/server_cron_test.gocovering unavailable manager, CRUD flow, invalid RFC3339at_time, not-found run-now, and disabled-job run-now behavior. - Added frontend Cron integration with new hooks and page (
pkg/webui/frontend/src/hooks/useCron.ts,pkg/webui/frontend/src/pages/CronPage.tsx), plus routing/sidebar wiring inpkg/webui/frontend/src/App.tsxandpkg/webui/frontend/src/components/layout/Sidebar.tsx. - Added Cron i18n strings in
pkg/webui/frontend/public/i18n/en.json,pkg/webui/frontend/public/i18n/zh-CN.json, andpkg/webui/frontend/public/i18n/ja.json. - Verification run:
go test ./pkg/webui ./pkg/cron ./pkg/agent ./pkg/config ./cmd/nekobotpassed. - Verification run:
go test ./...passed. - Verification run:
npm --prefix pkg/webui/frontend run buildpassed (after installing frontend deps withnpm --prefix pkg/webui/frontend ci). - Added CLI command
nekobot cron run <job-id>to trigger immediate execution for existing jobs. - Aligned blades runtime tool error semantics with legacy orchestrator: tool execution failures now return
Error: ...tool results instead of aborting the whole run. - Added blades runtime regression tests for tool-error result fallback and role/parts mapping (
pkg/agent/agent_test.go). - Updated architecture docs for Cron capabilities to reflect DB-backed persistence and run-now support.
- Verification run:
go test ./pkg/agent ./pkg/cron ./cmd/nekobotpassed. - Verification run:
go test ./...passed. - Added CLI regression tests for
nekobot cron runcommand wiring and arg validation incmd/nekobot/cron_test.go. - Verification run:
go test ./cmd/nekobotpassed. - Verification run:
go test ./...passed. - Fixed blades tool-result history conversion in
pkg/agent/blades_runtime.go: when hydrating priorRoleToolmessages, eachblades.ToolPartnow maps to its ownproviders.UnifiedMessageso multiple tool results in one blades message are preserved for provider context reconstruction. - Added regression tests for blades tool history conversion in
pkg/agent/agent_test.go:TestBladesModelProvider_ConvertMessagesPreservesMultipleToolResultsTestBladesModelProvider_ConvertMessagesToolFallbackToRequest
- Verification run:
go test ./pkg/agentpassed. - Verification run:
go test ./...passed. - Feature Batch #2 收口:
chatWithBladesOrchestrator会话历史注入现在保留 assistant 的 tool-calls turns(即使 text 为空),避免在重建 blades history 时丢失工具调用上下文,保证与 legacy 的 tool 执行链路语义一致。 - Added
hasBladesHistoryContent+ enhancedtoBladesMessageinpkg/agent/blades_runtime.goto preserve assistant tool call metadata when hydrating history into blades session. - Added regression tests in
pkg/agent/agent_test.go:TestToBladesMessage_AssistantToolCallsPreservedTestHasBladesHistoryContent
- Verification run:
go test -count=1 ./pkg/agentpassed. - Implemented static prompt caching in
pkg/agent/context.gowith file-state/tool-signature invalidation and dynamic current-time substitution to reduce repeated full prompt rebuilds while keeping fresh time output. - Added context prompt regression tests in
pkg/agent/agent_test.gofor current-time placeholder replacement plus cache invalidation on bootstrap file and tool-description changes. - Verification run:
go test -count=1 ./pkg/agentpassed. - Verification run:
go test -count=1 ./...passed.
-
Completed harness feature review and fixes:
- Reviewed 5 recent commits:
1b7c3d0,580741d,46026ac,583245d,c409cf1 - Fixed snapshot incremental delta calculation to avoid message duplication
- Added snapshot JSONL rewrite after
Undo()for persistence - Added session ID passthrough for streaming updates
- Added explicit fallback notice for streaming without handler
- Changed watcher path matching to use RLock for better concurrency
- Updated ConfigPage section metadata with proper i18n labels
- Committed and pushed all fixes as
7412fdd
- Reviewed 5 recent commits:
-
Verification run:
GOPROXY=https://goproxy.cn,direct go test -count=1 ./...passednpm --prefix pkg/webui/frontend run buildpassed
-
Updated task_plan.md:
- Marked Batch A verification complete
- Marked Batch C frontend build and backend test complete
- Marked 2026-03-29 review batch Phase 4 complete
-
Added missing i18n labels for harness config sections:
- Added audit, undo, preprocess, learnings, watch section labels
- Added translations for en, zh-CN, ja
- Committed as
28a8093
-
Added line range support for @file mentions:
- Support @file.txt:50-100 syntax for extracting specific line ranges
- Added extractLineRange method for line-based content extraction
- Updated formatFileReference to show line range in output
- Added test cases for line range functionality
- Committed as
6707d22
-
Verification run:
go test -count=1 ./pkg/preprocesspassednpm --prefix pkg/webui/frontend run buildpassed
-
Completed next browser advanced CDP action slice from
task_plan.md:- added
browseractionsget_metrics,emulate_device,set_viewport,list_pages,new_page,activate_page, andclose_pageinpkg/tools/browser.go. - extended browser tool parameter schema with the new actions plus
deviceprofile selection andtarget_idfor page control. - taught
BrowserSessionto expose a reusable DevTools management seam from the active attach endpoint, so higher-level CDP control actions do not need to reconstruct attach logic. - added deterministic helpers for device-profile / viewport validation and page-target control, with regression tests in
pkg/tools/browser_test.goandpkg/tools/browser_session_test.go.
- added
-
Verification run:
go test -count=1 ./pkg/toolspassed.
-
Completed follow-up browser cookie/session-control slice from
task_plan.md:- added
browseractionsset_cookieandclear_cookiesinpkg/tools/browser.go. - added cookie-parameter schema support for
name,value,secure,http_only, andsame_site. - added validation helpers for cookie-setting arguments and regression tests covering required fields, absolute URL enforcement, and SameSite normalization.
- added
-
Verification run:
go test -count=1 ./pkg/tools -run 'TestBrowserToolParametersIncludeCookieControlActions|TestBrowserToolBuildSetCookieArgs|TestBrowserToolBuildSetCookieArgsRejectsMissingName|TestBrowserToolBuildSetCookieArgsRejectsRelativeURL|TestBrowserToolBuildSetCookieArgsRejectsInvalidSameSite'passed.go test -count=1 ./pkg/toolspassed.
-
Completed follow-up browser storage control slice from
task_plan.md:- added
browseractionsget_storage,set_storage,remove_storage, andclear_storage. - kept the slice page-scoped and relay-friendly by implementing it as generated JavaScript over the already attached browser session, without reopening attach/session orchestration.
- added validation and script-shape regression tests for storage scope, key/value requirements, and generated JS behavior.
- added
-
Completed browser console capture closure that was already partially in flight:
- finished the missing
get_consolehelper types/options/collection utilities so the browser control surface compiles and returns bounded console/log entries. - kept the implementation minimal: level filtering + bounded collection over existing Runtime/Log subscriptions, without introducing persistent console session state.
- finished the missing
-
Verification run:
go test -count=1 ./pkg/toolspassed.
-
Completed browser storage/console control follow-up slice:
- wired browser storage actions (
get_storage,set_storage,remove_storage,clear_storage) into the runtime execute switch and restored their JS script builder implementation. - extended
get_storageso it can return either the full storage map or a single key lookup. - implemented minimal
get_consolelog capture with level filtering and bounded result size so the already-exposed browser action is now functional.
- wired browser storage actions (
-
Verification run:
go test -count=1 ./pkg/toolspassed.
-
Completed follow-up browser cookie/session-control slice from
task_plan.md:- added
browseractionsset_cookieandclear_cookiesinpkg/tools/browser.go. - added cookie-parameter schema support for
name,text,secure,http_only, andsame_site. - added validation helpers for cookie-setting arguments and regression tests covering required fields, absolute URL enforcement, and SameSite normalization.
- added
-
Verification run:
go test -count=1 ./pkg/tools -run 'TestBrowserToolParametersIncludeCookieControlActions|TestBrowserToolBuildSetCookieArgs|TestBrowserToolBuildSetCookieArgsRejectsMissingName|TestBrowserToolBuildSetCookieArgsRejectsRelativeURL|TestBrowserToolBuildSetCookieArgsRejectsInvalidSameSite'passed.go test -count=1 ./pkg/toolspassed.
-
Completed follow-up browser relay/CDP slice from
task_plan.md:- added
browseractionget_consoleinpkg/tools/browser.go. - added console filter options
errors_only,warnings_only,info_only, andmax_entries. - added deterministic console entry normalization helpers for log/runtime events with regression tests in
pkg/tools/browser_test.go.
- added
-
Verification run:
go test -count=1 ./pkg/tools -run 'TestBrowserToolParametersIncludeGetConsole|TestBrowserToolBuildConsoleOptionsDefaultsAndFilters|TestBrowserConsoleEntryMatchesRespectsPriority|TestBrowserConsoleEntryFrom(Log|Runtime)'passed.go test -count=1 ./pkg/toolspassed.
-
Completed browser minimal network-observation slice from
task_plan.md:- added
browseractionget_networkinpkg/tools/browser.go. - implemented a bounded request/response/finished/failed event collector using the existing CDP session without expanding into HAR/interception.
- added regression tests for network action exposure and event-summary shaping.
- added
-
Verification run:
go test -count=1 ./pkg/tools -run 'TestBrowserToolParametersIncludeGetNetwork|TestBrowserNetworkEntryFromRequest|TestBrowserNetworkEntryFromResponse|TestBrowserNetworkEntryFromFinishedAndFailed'passed.go test -count=1 ./pkg/toolspassed.
-
Completed gateway control-plane hardening phase 18 (metrics auth gate):
- moved
GET /metricsbehind the same authenticated control-plane gate as the rest of the gateway admin surface. - kept the scope intentionally narrow by requiring manage-level access only, without changing metric shape or pairing semantics.
- moved
-
Verification run:
go test -count=1 ./pkg/gateway -run 'TestMetricsEndpoint(RequiresAuth|RejectsMemberRole)?$|TestMetricsEndpoint$'passed.go test -count=1 ./pkg/gatewaypassed.
-
Completed externalagent consumer phase 6 (gateway approval UX closure):
- gateway now exposes approval endpoints for the externalagent flow (
GET /api/v1/approvals,POST /api/v1/approvals/:id/approve,POST /api/v1/approvals/:id/deny). - pending gateway externalagent launches can now be approved and immediately continued to process start without leaving the gateway control plane.
- gateway now exposes approval endpoints for the externalagent flow (
-
Verification run:
go test -count=1 ./pkg/gateway -run 'Test(GatewayApproveExternalAgentPendingRequestStartsProcessImmediately|GatewayListApprovalsReturnsPendingRequests|ResolveExternalAgentSessionEndpoint(ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByApprovalMode|StartsProcessWhenApproved))$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent consumer phase 5 (WeChat shared starter adoption):
- WeChat
codexruntime creation now uses the shared externalagent process starter instead of its own private process-start path. - this is the first channel-side adoption of the shared starter layer.
- WeChat
-
Verification run:
go test -count=1 ./pkg/channels/wechat -run 'Test(BuildRuntimePresetCodex.*|ControlServiceCreateCodexRuntime.*)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechatpassed.
-
Completed externalagent approval UX phase 2 (shared HTTP response body helper):
- added shared
ResolveFlowResult.HTTPStatus()andResolveFlowResult.ResponseBody()helpers so WebUI and gateway now emit their externalagent resolve responses from the same contract helper instead of hand-assembling maps separately.
- added shared
-
Verification run:
go test -count=1 ./pkg/webui ./pkg/gateway -run 'Test(HandleResolveExternalAgentSession(CreatesSession|IncludesMatchedPermissionRulePreview|ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByPermissionRule|StartsProcessWhenApproved)|ApproveExternalAgentPendingRequest(StartsProcessImmediately|AllowsSubsequentResolveForSameSession)|ResolveExternalAgentSessionEndpoint(CreatesSession|ResolvesRelativeWorkspace|RejectsWorkspaceOutsideConfiguredRoot|IncludesMatchedPermissionRulePreview|ReturnsPendingApprovalForAskRule|ReturnsPendingApprovalForManualMode|RejectsDeniedByApprovalMode|StartsProcessWhenApproved)|GatewayApproveExternalAgentPendingRequestStartsProcessImmediately|GatewayListApprovalsReturnsPendingRequests)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechat ./pkg/agentpassed.
-
Completed externalagent approval UX phase 1 (consumer contract convergence):
- aligned the three main consumers around the same externalagent approval concepts:
status,request_id,reason, andlaunch_policy. - WeChat now exposes the same pending/denied semantics through a standardized text reply helper instead of ad-hoc phrasing.
- aligned the three main consumers around the same externalagent approval concepts:
-
Verification run:
go test -count=1 ./pkg/channels/wechat -run 'Test(ControlServiceResolveCodexRuntimeReturnsPendingApproval|ControlServiceResolveCodexRuntimeReturnsDeniedApproval|FormatRuntimeApprovalReply(Pending|Denied)|BuildRuntimePresetCodex.*|ControlServiceCreateCodexRuntime.*)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechat ./pkg/agentpassed.
-
Completed externalagent consumer phase 7 (WeChat shared resolve orchestrator adoption):
- WeChat codex runtime creation now uses the shared externalagent resolve orchestrator for approval/policy gating before process start.
- this brings the third consumer into the shared resolve layer instead of only reusing normalization/starter helpers.
-
Verification run:
go test -count=1 ./pkg/channels/wechat ./pkg/agent ./pkg/externalagentpassed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechat ./pkg/agentpassed.
-
Evaluated
/home/czyt/code/fastclawfor importable capabilities and added prioritized adoption candidates totask_plan.md. -
Recommended follow-up parallel plan:
- Hook Pipeline
- Webhook Trigger Server
- Policy Engine
- Setup Wizard / First-Run
- OpenAI-Compatible API Hardening
-
User clarified an additional requirement for all fastclaw-derived work: pair backend implementation with corresponding
nekobotfrontend completion (management, observability, or interaction UI), rather than landing backend-only features. -
Current fastclaw-derived execution status:
- Hook Pipeline landed
- Webhook Trigger Server landed
- Policy Engine landed
- Setup Wizard / First-Run landed
- OpenAI-compatible hardening started (first header-hardening slice landed)
-
Completed WeChat approval UX phase 1 (managed externalagent /yes /no closure):
- WeChat now resolves pending managed externalagent launches through the existing
/yes,/no, and/selectinteraction surface instead of only showing a pending message. - approving a pending managed launch now immediately continues startup; denying keeps the process stopped.
- WeChat now resolves pending managed externalagent launches through the existing
-
Verification run:
go test -count=1 ./pkg/channels/wechat -run 'Test(ControlServiceResolvePendingInteraction(ContinuesManagedRuntime|DeniesManagedRuntime)|ResolvePendingInteractionDelegatesToRuntimeApprovals|ResolvePendingInteractionDelegatesNumericSelectToRuntimeApprovals)$'passed.go test -count=1 ./pkg/gateway ./pkg/externalagent ./pkg/webui ./pkg/channels/wechat ./pkg/agent ./pkg/tools ./pkg/approval ./pkg/tasks ./pkg/policypassed.
-
Completed channel trace follow-up phase 2 (shared tool_call trace helper + ServerChan adoption):
- extracted the WeChat-only tool trace formatter into shared
pkg/channeltraceso direct session-based channels can reuse the sametool_call/tool_resultrendering contract. - ServerChan now prepends the same compact tool trace before the final reply, and WeChat now consumes the shared helper instead of keeping a private implementation.
- kept the slice intentionally narrow: only direct
ChatWithPromptContextchannels were touched; bus-driven channels like Telegram still need a separate event-path design to surface tool traces.
- extracted the WeChat-only tool trace formatter into shared
-
Verification run:
go test -count=1 ./pkg/channeltrace ./pkg/channels/wechat ./pkg/channels/serverchanpassed.