@moretea-labs/forge 1.5.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +2 -0
- package/DESIGN.md +7 -2
- package/README.md +4 -4
- package/README.zh-CN.md +2 -2
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/pre-edit-guard.sh +8 -39
- package/assets/hooks/prompt-guard.sh +7 -83
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +54 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/sprint-backlog.sh +6 -6
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +170 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +38 -22
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +3 -3
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Installation.md +1 -1
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-public-docs.sh +14 -1
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -1
- package/scripts/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/chatgpt.ts +31 -2
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +37 -10
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +106 -3
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +898 -430
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/github.ts +133 -0
- package/src/cli/hook/prompt-guard-decision.ts +17 -18
- package/src/cli/hook/prompt-intents.ts +1 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +225 -67
- package/src/cli/local-bridge/console-view-models.ts +3 -35
- package/src/cli/local-bridge/facade-api.ts +2 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/styles.css +27 -11
- package/src/cli/local-bridge/frontend/src/types.ts +8 -7
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +29 -532
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/paths.ts +8 -2
- package/src/cli/mcp/repository-tools.ts +356 -101
- package/src/cli/mcp/server.ts +625 -54
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +125 -15
- package/src/cli/mcp/transports/session-registry.ts +67 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +209 -14
- package/src/cli/repositories/command-executor.ts +55 -521
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +121 -0
- package/src/cli/repositories/safe-patch.ts +6 -4
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +110 -10
- package/src/cli/repository/inspector.ts +565 -62
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation.ts +275 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/validation.ts +15 -2
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
- package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
- package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +55 -11
- package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/routing/route-policy.ts +7 -16
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +19 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
- package/src/runtime/execution/process-runtime/check-result.ts +63 -2
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
- package/src/runtime/execution/process-runtime/gc.ts +98 -12
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
- package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
- package/src/runtime/execution/process-runtime/runtime.ts +59 -4
- package/src/runtime/execution/process-runtime/store.ts +7 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +185 -83
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
- package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2925 -318
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +6 -31
- package/src/runtime/plugins/browser-handoff.ts +92 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-registration.ts +84 -5
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +4 -2
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/gmail-adapter.ts +28 -2
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +125 -8
- package/src/runtime/plugins/macos-capability-broker.ts +55 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +366 -24
- package/src/runtime/plugins/types.ts +39 -2
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/actions.ts +0 -10
- package/src/runtime/recovery/classifier.ts +0 -9
- package/src/runtime/recovery/maintenance-executor.ts +415 -18
- package/src/runtime/recovery/probe.ts +0 -1
- package/src/runtime/recovery/types.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +162 -19
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -27
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/index.ts +0 -1
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/safe-tooling/types.ts +0 -30
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +702 -94
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
- package/src/runtime/workflow/schedules/engine.ts +509 -80
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- package/src/runtime/workflow/schedules/types.ts +6 -3
- package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/bin/forge-desktop-helper.mjs +0 -204
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
- package/docs/architecture/current/codegraph-context-provider.md +0 -226
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
- package/docs/architecture/current/entity-model.md +0 -592
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -144
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -311
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
- package/docs/operations/controller-desktop-plugin.md +0 -112
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/plugins/desktop-adapter.ts +0 -374
- package/src/runtime/safe-tooling/browser-safe.ts +0 -133
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AssistantPluginActionDescriptor,
|
|
3
|
+
AssistantPluginActionExecutionInput,
|
|
4
|
+
AssistantPluginCapability,
|
|
5
|
+
AssistantPluginHealth,
|
|
6
|
+
AssistantPluginManifest,
|
|
7
|
+
AssistantPluginPermissionScope,
|
|
8
|
+
} from './types';
|
|
9
|
+
import { AssistantPluginError } from './errors';
|
|
10
|
+
import { executeBrowserPluginAction } from './browser-adapter';
|
|
11
|
+
|
|
12
|
+
const PLUGIN_ID = 'xiaohongshu';
|
|
13
|
+
const RECIPE_VERSION = 7;
|
|
14
|
+
const CREATOR_BASE_URL = 'https://creator.xiaohongshu.com/publish/publish?source=official';
|
|
15
|
+
const CREATOR_ARTICLE_URL = `${CREATOR_BASE_URL}&target=article`;
|
|
16
|
+
const CREATOR_NOTE_MANAGER_URL = 'https://creator.xiaohongshu.com/new/note-manager';
|
|
17
|
+
const IMAGE_TAB_TEXT = '上传图文';
|
|
18
|
+
const ARTICLE_NEW_TEXT = '新的创作';
|
|
19
|
+
const ARTICLE_LAYOUT_TEXT = '一键排版';
|
|
20
|
+
const ARTICLE_NEXT_TEXT = '下一步';
|
|
21
|
+
const IMAGE_FILE_SELECTOR = 'input[type=file]';
|
|
22
|
+
const IMAGE_TITLE_SELECTOR = 'input[placeholder*="标题"]';
|
|
23
|
+
const IMAGE_BODY_SELECTOR = '[contenteditable="true"][role="textbox"]';
|
|
24
|
+
const ARTICLE_TITLE_SELECTOR = 'textarea:nth-of-type(1)';
|
|
25
|
+
const ARTICLE_BODY_SELECTOR = '[contenteditable="true"]';
|
|
26
|
+
const PUBLISH_SELECTOR = 'xhs-publish-btn';
|
|
27
|
+
const PUBLISH_EVENT = 'publish';
|
|
28
|
+
|
|
29
|
+
const LOGIN_URL_MARKERS = ['/login', 'passport.xiaohongshu.com', 'login.xiaohongshu.com'];
|
|
30
|
+
const LOGIN_TEXT_MARKERS = ['手机号登录', '扫码登录', '验证码登录'];
|
|
31
|
+
|
|
32
|
+
type PublishMode = 'image_note' | 'generated_image_note' | 'long_text';
|
|
33
|
+
type NormalizedPublishMode = 'image_note' | 'long_text';
|
|
34
|
+
type RecipeStep = {
|
|
35
|
+
id: string;
|
|
36
|
+
actionId: string;
|
|
37
|
+
args: Record<string, unknown>;
|
|
38
|
+
expectation?: string;
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
interface XiaohongshuHooks {
|
|
42
|
+
executeBrowserAction?: typeof executeBrowserPluginAction;
|
|
43
|
+
now?: () => string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
let hooks: XiaohongshuHooks = {};
|
|
47
|
+
|
|
48
|
+
export function setXiaohongshuPluginHooksForTest(next: XiaohongshuHooks): void {
|
|
49
|
+
hooks = next;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function resetXiaohongshuPluginHooksForTest(): void {
|
|
53
|
+
hooks = {};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function now(): string {
|
|
57
|
+
return hooks.now?.() ?? new Date().toISOString();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function browserExecutor(): typeof executeBrowserPluginAction {
|
|
61
|
+
return hooks.executeBrowserAction ?? executeBrowserPluginAction;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function stringValue(value: unknown): string | undefined {
|
|
65
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function requiredString(value: unknown, name: string): string {
|
|
69
|
+
const normalized = stringValue(value);
|
|
70
|
+
if (!normalized) throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', `${name} is required.`, { retryable: false });
|
|
71
|
+
return normalized;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function stringList(value: unknown): string[] {
|
|
75
|
+
if (value === undefined) return [];
|
|
76
|
+
if (!Array.isArray(value) || value.some((entry) => typeof entry !== 'string' || !entry.trim())) {
|
|
77
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'image_paths must be a string array.', { retryable: false });
|
|
78
|
+
}
|
|
79
|
+
return value.map((entry) => String(entry).trim());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function publishMode(value: unknown): PublishMode {
|
|
83
|
+
if (value === 'image_note' || value === 'generated_image_note' || value === 'long_text') return value;
|
|
84
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'mode must be image_note, generated_image_note, or long_text.', { retryable: false });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function normalizedMode(mode: PublishMode): NormalizedPublishMode {
|
|
88
|
+
return mode === 'generated_image_note' ? 'image_note' : mode;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function validProfileUrl(value: string): boolean {
|
|
92
|
+
try {
|
|
93
|
+
const parsed = new URL(value);
|
|
94
|
+
return parsed.protocol === 'https:' && parsed.hostname === 'www.xiaohongshu.com' && parsed.pathname.startsWith('/user/profile/');
|
|
95
|
+
} catch {
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export function isXiaohongshuAuthRequired(url: string, text: string): boolean {
|
|
101
|
+
const normalizedUrl = url.toLowerCase();
|
|
102
|
+
return LOGIN_URL_MARKERS.some((marker) => normalizedUrl.includes(marker.toLowerCase()))
|
|
103
|
+
|| LOGIN_TEXT_MARKERS.some((marker) => text.includes(marker));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function classifyXiaohongshuPublishState(input: {
|
|
107
|
+
phase: 'preflight' | 'creator_receipt' | 'profile_verify';
|
|
108
|
+
url: string;
|
|
109
|
+
text: string;
|
|
110
|
+
expectedTitle?: string;
|
|
111
|
+
}): 'AUTH_REQUIRED' | 'READY' | 'PUBLISHED_RECEIPT' | 'PROFILE_VERIFIED' | 'VERIFY_PENDING' | 'PAGE_SCHEMA_CHANGED' {
|
|
112
|
+
if (isXiaohongshuAuthRequired(input.url, input.text)) return 'AUTH_REQUIRED';
|
|
113
|
+
if (input.phase === 'creator_receipt') {
|
|
114
|
+
const normalizedUrl = input.url.toLowerCase();
|
|
115
|
+
return normalizedUrl.includes('published=true')
|
|
116
|
+
|| normalizedUrl.includes('/publish/success')
|
|
117
|
+
|| normalizedUrl.includes('/publish/editsuccess')
|
|
118
|
+
? 'PUBLISHED_RECEIPT'
|
|
119
|
+
: 'VERIFY_PENDING';
|
|
120
|
+
}
|
|
121
|
+
if (input.phase === 'profile_verify') {
|
|
122
|
+
return input.expectedTitle && input.text.includes(input.expectedTitle) ? 'PROFILE_VERIFIED' : 'VERIFY_PENDING';
|
|
123
|
+
}
|
|
124
|
+
return input.text.includes('创作服务平台') || input.text.includes('发布笔记') ? 'READY' : 'PAGE_SCHEMA_CHANGED';
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function parseRecipeArgs(args: Record<string, unknown>) {
|
|
128
|
+
const mode = publishMode(args.mode);
|
|
129
|
+
const sessionId = requiredString(args.session_id, 'session_id');
|
|
130
|
+
const profileUrl = requiredString(args.profile_url, 'profile_url');
|
|
131
|
+
if (!validProfileUrl(profileUrl)) {
|
|
132
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'profile_url must be an https://www.xiaohongshu.com/user/profile/... URL.', { retryable: false });
|
|
133
|
+
}
|
|
134
|
+
const title = requiredString(args.title, 'title');
|
|
135
|
+
const body = requiredString(args.body, 'body');
|
|
136
|
+
const imagePaths = stringList(args.image_paths);
|
|
137
|
+
const summary = stringValue(args.summary);
|
|
138
|
+
const templateText = stringValue(args.template_text);
|
|
139
|
+
if (normalizedMode(mode) === 'image_note' && mode !== 'generated_image_note' && (imagePaths.length < 1 || imagePaths.length > 18)) {
|
|
140
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'image_note requires 1-18 image_paths.', { retryable: false });
|
|
141
|
+
}
|
|
142
|
+
if (mode === 'generated_image_note' && imagePaths.length > 18) {
|
|
143
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'generated_image_note accepts at most 18 generated image_paths.', { retryable: false });
|
|
144
|
+
}
|
|
145
|
+
return { mode, normalizedMode: normalizedMode(mode), sessionId, profileUrl, title, body, imagePaths, summary, templateText };
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function buildXiaohongshuPublishRecipe(args: Record<string, unknown>): Record<string, unknown> {
|
|
149
|
+
const parsed = parseRecipeArgs(args);
|
|
150
|
+
const generationRequired = parsed.mode === 'generated_image_note' && parsed.imagePaths.length === 0;
|
|
151
|
+
if (generationRequired) {
|
|
152
|
+
return {
|
|
153
|
+
schemaVersion: 1,
|
|
154
|
+
recipeVersion: RECIPE_VERSION,
|
|
155
|
+
provider: 'xiaohongshu-web',
|
|
156
|
+
requestedMode: parsed.mode,
|
|
157
|
+
normalizedMode: parsed.normalizedMode,
|
|
158
|
+
generationRequired: true,
|
|
159
|
+
sessionId: parsed.sessionId,
|
|
160
|
+
profileUrl: parsed.profileUrl,
|
|
161
|
+
generationHandoff: {
|
|
162
|
+
status: 'required',
|
|
163
|
+
requiredInput: 'image_paths',
|
|
164
|
+
minImages: 1,
|
|
165
|
+
maxImages: 18,
|
|
166
|
+
resumeAction: 'publish_note',
|
|
167
|
+
resumeMode: 'generated_image_note',
|
|
168
|
+
next: 'Generate one or more image files, then call publish_note again with image_paths. The publishing path will normalize to image_note.',
|
|
169
|
+
},
|
|
170
|
+
steps: [],
|
|
171
|
+
verification: [],
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const creatorUrl = parsed.normalizedMode === 'long_text' ? CREATOR_ARTICLE_URL : CREATOR_BASE_URL;
|
|
176
|
+
const steps: RecipeStep[] = [
|
|
177
|
+
{
|
|
178
|
+
id: 'preflight.navigate_creator',
|
|
179
|
+
actionId: 'navigate',
|
|
180
|
+
args: { session_id: parsed.sessionId, url: creatorUrl, wait_until: 'domcontentloaded', timeout_ms: 60_000 },
|
|
181
|
+
expectation: 'Creator page remains in the persisted browser profile; raw cookies/tokens are not copied into recipe state.',
|
|
182
|
+
},
|
|
183
|
+
parsed.normalizedMode === 'image_note'
|
|
184
|
+
? {
|
|
185
|
+
id: 'image.select_mode',
|
|
186
|
+
actionId: 'click_text',
|
|
187
|
+
args: { session_id: parsed.sessionId, text: IMAGE_TAB_TEXT, post_action_wait_ms: 750 },
|
|
188
|
+
expectation: 'Creator may default to 上传视频; switch semantically to the exact visible 上传图文 tab before waiting for the image input.',
|
|
189
|
+
}
|
|
190
|
+
: {
|
|
191
|
+
id: 'preflight.read_auth_state',
|
|
192
|
+
actionId: 'get_text',
|
|
193
|
+
args: { session_id: parsed.sessionId, max_chars: 8_000 },
|
|
194
|
+
expectation: 'Classify with phase=preflight; stop as AUTH_REQUIRED before editing if login markers appear.',
|
|
195
|
+
},
|
|
196
|
+
];
|
|
197
|
+
|
|
198
|
+
if (parsed.normalizedMode === 'image_note') {
|
|
199
|
+
steps.push(
|
|
200
|
+
{ id: 'image.wait_file_input', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: IMAGE_FILE_SELECTOR, state: 'attached', timeout_ms: 30_000 }, expectation: 'Wait for the image-upload input only after the semantic 上传图文 mode switch.' },
|
|
201
|
+
{ id: 'image.attach_files', actionId: 'attach_local_file', args: { session_id: parsed.sessionId, selector: IMAGE_FILE_SELECTOR, file_paths: parsed.imagePaths, post_action_wait_ms: 1_200 }, expectation: 'One input event/change event after all files are selected.' },
|
|
202
|
+
{ id: 'image.wait_editor', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: IMAGE_TITLE_SELECTOR, state: 'visible', timeout_ms: 30_000 } },
|
|
203
|
+
{ id: 'image.fill_title', actionId: 'fill', args: { session_id: parsed.sessionId, selector: IMAGE_TITLE_SELECTOR, text: parsed.title } },
|
|
204
|
+
{ id: 'image.fill_body', actionId: 'fill', args: { session_id: parsed.sessionId, selector: IMAGE_BODY_SELECTOR, text: parsed.body } },
|
|
205
|
+
);
|
|
206
|
+
} else {
|
|
207
|
+
steps.push(
|
|
208
|
+
{ id: 'article.new', actionId: 'click_text', args: { session_id: parsed.sessionId, text: ARTICLE_NEW_TEXT, post_action_wait_ms: 900 }, expectation: 'Exact visible 新的创作 button.' },
|
|
209
|
+
{ id: 'article.wait_editor', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: ARTICLE_TITLE_SELECTOR, state: 'visible', timeout_ms: 30_000 } },
|
|
210
|
+
{ id: 'article.fill_title', actionId: 'fill', args: { session_id: parsed.sessionId, selector: ARTICLE_TITLE_SELECTOR, text: parsed.title } },
|
|
211
|
+
{ id: 'article.fill_body', actionId: 'fill', args: { session_id: parsed.sessionId, selector: ARTICLE_BODY_SELECTOR, text: parsed.body } },
|
|
212
|
+
{ id: 'article.layout', actionId: 'click_text', args: { session_id: parsed.sessionId, text: ARTICLE_LAYOUT_TEXT, post_action_wait_ms: 1_500 } },
|
|
213
|
+
);
|
|
214
|
+
if (parsed.templateText) {
|
|
215
|
+
steps.push({ id: 'article.select_template', actionId: 'click_text', args: { session_id: parsed.sessionId, text: parsed.templateText, post_action_wait_ms: 500 } });
|
|
216
|
+
}
|
|
217
|
+
steps.push(
|
|
218
|
+
{ id: 'article.next', actionId: 'click_text', args: { session_id: parsed.sessionId, text: ARTICLE_NEXT_TEXT, post_action_wait_ms: 1_200 } },
|
|
219
|
+
{ id: 'article.wait_publish', actionId: 'wait_for_selector', args: { session_id: parsed.sessionId, selector: PUBLISH_SELECTOR, state: 'visible', timeout_ms: 30_000 } },
|
|
220
|
+
);
|
|
221
|
+
if (parsed.summary) {
|
|
222
|
+
steps.push({ id: 'article.fill_summary', actionId: 'fill', args: { session_id: parsed.sessionId, selector: ARTICLE_BODY_SELECTOR, text: parsed.summary } });
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
steps.push(
|
|
227
|
+
{ id: 'publish.semantic_submit', actionId: 'dispatch_event', args: { session_id: parsed.sessionId, selector: PUBLISH_SELECTOR, event: PUBLISH_EVENT, post_action_wait_ms: 1_500 }, expectation: 'Current XHS closed-shadow publish control emits publish; recipe version must be updated if this semantic event changes.' },
|
|
228
|
+
{ id: 'verify.creator_receipt', actionId: 'get_text', args: { session_id: parsed.sessionId, max_chars: 4_000 }, expectation: 'Creator URL must expose a recognized publish success receipt before title verification.' },
|
|
229
|
+
{ id: 'verify.creator_note_manager', actionId: 'navigate', args: { session_id: parsed.sessionId, url: CREATOR_NOTE_MANAGER_URL, wait_until: 'domcontentloaded', timeout_ms: 60_000 } },
|
|
230
|
+
{ id: 'verify.creator_title', actionId: 'get_text', args: { session_id: parsed.sessionId, max_chars: 12_000 }, expectation: `Creator note manager text must contain exact title even when the note is still under review: ${parsed.title}` },
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
return {
|
|
234
|
+
schemaVersion: 1,
|
|
235
|
+
recipeVersion: RECIPE_VERSION,
|
|
236
|
+
provider: 'xiaohongshu-web',
|
|
237
|
+
requestedMode: parsed.mode,
|
|
238
|
+
normalizedMode: parsed.normalizedMode,
|
|
239
|
+
generationRequired: false,
|
|
240
|
+
sessionId: parsed.sessionId,
|
|
241
|
+
profileUrl: parsed.profileUrl,
|
|
242
|
+
authPolicy: {
|
|
243
|
+
loginUrlMarkers: LOGIN_URL_MARKERS,
|
|
244
|
+
loginTextMarkers: LOGIN_TEXT_MARKERS,
|
|
245
|
+
credentialPersistence: 'browser_profile_only',
|
|
246
|
+
onAuthRequired: 'stop_before_edit_and_resume_after_user_login',
|
|
247
|
+
},
|
|
248
|
+
selectors: {
|
|
249
|
+
imageFile: IMAGE_FILE_SELECTOR,
|
|
250
|
+
imageTitle: IMAGE_TITLE_SELECTOR,
|
|
251
|
+
imageBody: IMAGE_BODY_SELECTOR,
|
|
252
|
+
articleTitle: ARTICLE_TITLE_SELECTOR,
|
|
253
|
+
articleBody: ARTICLE_BODY_SELECTOR,
|
|
254
|
+
publish: PUBLISH_SELECTOR,
|
|
255
|
+
publishEvent: PUBLISH_EVENT,
|
|
256
|
+
},
|
|
257
|
+
steps,
|
|
258
|
+
verification: ['creator_publish_success_receipt', 'creator_note_manager_contains_exact_title'],
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
function resultText(result: Record<string, unknown>): string {
|
|
263
|
+
return typeof result.text === 'string' ? result.text : '';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function resultUrl(result: Record<string, unknown>): string {
|
|
267
|
+
if (typeof result.url === 'string') return result.url;
|
|
268
|
+
const session = result.session;
|
|
269
|
+
if (session && typeof session === 'object' && typeof (session as Record<string, unknown>).url === 'string') return String((session as Record<string, unknown>).url);
|
|
270
|
+
const navigation = result.navigation;
|
|
271
|
+
if (navigation && typeof navigation === 'object' && typeof (navigation as Record<string, unknown>).url === 'string') return String((navigation as Record<string, unknown>).url);
|
|
272
|
+
return '';
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function schemaDriftMessage(message: string): boolean {
|
|
276
|
+
return /not found|does not allow multiple|Timeout waiting for selector|visible exact text not found|file input/i.test(message);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export async function executeXiaohongshuPluginAction(input: AssistantPluginActionExecutionInput): Promise<Record<string, unknown>> {
|
|
280
|
+
if (input.actionId === 'get_publish_recipe') return buildXiaohongshuPublishRecipe(input.args);
|
|
281
|
+
if (input.actionId === 'classify_publish_state') {
|
|
282
|
+
const phase = input.args.phase;
|
|
283
|
+
if (phase !== 'preflight' && phase !== 'creator_receipt' && phase !== 'profile_verify') {
|
|
284
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'phase must be preflight, creator_receipt, or profile_verify.', { retryable: false });
|
|
285
|
+
}
|
|
286
|
+
const url = requiredString(input.args.url, 'url');
|
|
287
|
+
const text = typeof input.args.text === 'string' ? input.args.text : '';
|
|
288
|
+
const expectedTitle = stringValue(input.args.expected_title);
|
|
289
|
+
return { state: classifyXiaohongshuPublishState({ phase, url, text, expectedTitle }), recipeVersion: RECIPE_VERSION };
|
|
290
|
+
}
|
|
291
|
+
if (input.actionId !== 'publish_note') {
|
|
292
|
+
throw new AssistantPluginError('PLUGIN_ACTION_NOT_SUPPORTED', `xiaohongshu/${input.actionId} is not supported.`, { retryable: false });
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
const parsed = parseRecipeArgs(input.args);
|
|
296
|
+
if (parsed.mode === 'generated_image_note' && parsed.imagePaths.length === 0) {
|
|
297
|
+
return {
|
|
298
|
+
status: 'generation_required',
|
|
299
|
+
recipeVersion: RECIPE_VERSION,
|
|
300
|
+
resumeMode: 'generated_image_note',
|
|
301
|
+
next: 'Generate one or more image files, then call publish_note again with image_paths. The publishing path will normalize to image_note.',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
const recipe = buildXiaohongshuPublishRecipe(input.args);
|
|
306
|
+
const steps = recipe.steps as RecipeStep[];
|
|
307
|
+
const execute = browserExecutor();
|
|
308
|
+
const receipts: Array<{ stepId: string; actionId: string; url?: string }> = [];
|
|
309
|
+
let currentStep = 'preflight';
|
|
310
|
+
let creatorReceipt: Record<string, unknown> | undefined;
|
|
311
|
+
let profileReceipt: Record<string, unknown> | undefined;
|
|
312
|
+
|
|
313
|
+
const runStep = async (step: RecipeStep, index: number): Promise<Record<string, unknown>> => {
|
|
314
|
+
currentStep = step.id;
|
|
315
|
+
const result = await execute({
|
|
316
|
+
...input,
|
|
317
|
+
pluginId: 'browser',
|
|
318
|
+
actionId: step.actionId,
|
|
319
|
+
requestId: `${input.requestId}:xhs:${RECIPE_VERSION}:${index}:${step.actionId}`,
|
|
320
|
+
args: step.args,
|
|
321
|
+
});
|
|
322
|
+
receipts.push({ stepId: step.id, actionId: step.actionId, ...(resultUrl(result) ? { url: resultUrl(result) } : {}) });
|
|
323
|
+
return result;
|
|
324
|
+
};
|
|
325
|
+
|
|
326
|
+
try {
|
|
327
|
+
const preflightNavigate = await runStep(steps[0], 0);
|
|
328
|
+
const navigateUrl = resultUrl(preflightNavigate);
|
|
329
|
+
if (isXiaohongshuAuthRequired(navigateUrl, '')) {
|
|
330
|
+
return {
|
|
331
|
+
status: 'auth_required',
|
|
332
|
+
recipeVersion: RECIPE_VERSION,
|
|
333
|
+
checkpoint: 'preflight.navigate_creator',
|
|
334
|
+
sessionId: parsed.sessionId,
|
|
335
|
+
next: 'Complete only the necessary Xiaohongshu login/verification in the existing browser profile, then rerun publish_note with the same inputs.',
|
|
336
|
+
receipts,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const preflight = await runStep(steps[1], 1);
|
|
341
|
+
const preflightUrl = resultUrl(preflight) || navigateUrl;
|
|
342
|
+
const preflightState = parsed.normalizedMode === 'image_note'
|
|
343
|
+
? 'READY'
|
|
344
|
+
: classifyXiaohongshuPublishState({ phase: 'preflight', url: preflightUrl, text: resultText(preflight) });
|
|
345
|
+
if (preflightState === 'AUTH_REQUIRED') {
|
|
346
|
+
return {
|
|
347
|
+
status: 'auth_required',
|
|
348
|
+
recipeVersion: RECIPE_VERSION,
|
|
349
|
+
checkpoint: steps[1].id,
|
|
350
|
+
sessionId: parsed.sessionId,
|
|
351
|
+
next: 'Complete only the necessary Xiaohongshu login/verification in the existing browser profile, then rerun publish_note with the same inputs.',
|
|
352
|
+
receipts,
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
if (preflightState !== 'READY') {
|
|
356
|
+
return { status: 'page_schema_changed', recipeVersion: RECIPE_VERSION, checkpoint: currentStep, receipts };
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
for (let index = 2; index < steps.length; index += 1) {
|
|
360
|
+
const step = steps[index];
|
|
361
|
+
const result = await runStep(step, index);
|
|
362
|
+
if (step.id === 'verify.creator_receipt') {
|
|
363
|
+
creatorReceipt = result;
|
|
364
|
+
const state = classifyXiaohongshuPublishState({ phase: 'creator_receipt', url: resultUrl(result), text: resultText(result) });
|
|
365
|
+
if (state === 'AUTH_REQUIRED') {
|
|
366
|
+
return { status: 'auth_required', recipeVersion: RECIPE_VERSION, checkpoint: step.id, receipts, next: 'Restore Xiaohongshu login in the existing browser profile, then verify the draft/publication state before retrying.' };
|
|
367
|
+
}
|
|
368
|
+
if (state !== 'PUBLISHED_RECEIPT') {
|
|
369
|
+
return { status: 'publish_unverified', recipeVersion: RECIPE_VERSION, checkpoint: step.id, creatorUrl: resultUrl(result), receipts };
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (step.id === 'verify.creator_title') profileReceipt = result;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const profileState = profileReceipt
|
|
376
|
+
? classifyXiaohongshuPublishState({ phase: 'profile_verify', url: resultUrl(profileReceipt), text: resultText(profileReceipt), expectedTitle: parsed.title })
|
|
377
|
+
: 'VERIFY_PENDING';
|
|
378
|
+
if (profileState !== 'PROFILE_VERIFIED') {
|
|
379
|
+
return {
|
|
380
|
+
status: 'verification_pending',
|
|
381
|
+
recipeVersion: RECIPE_VERSION,
|
|
382
|
+
creatorReceipt: creatorReceipt ? { url: resultUrl(creatorReceipt) } : undefined,
|
|
383
|
+
verificationUrl: CREATOR_NOTE_MANAGER_URL,
|
|
384
|
+
expectedTitle: parsed.title,
|
|
385
|
+
receipts,
|
|
386
|
+
};
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
status: 'published',
|
|
390
|
+
recipeVersion: RECIPE_VERSION,
|
|
391
|
+
mode: parsed.mode,
|
|
392
|
+
normalizedMode: parsed.normalizedMode,
|
|
393
|
+
title: parsed.title,
|
|
394
|
+
creatorReceipt: { url: resultUrl(creatorReceipt ?? {}) },
|
|
395
|
+
creatorVerification: { url: resultUrl(profileReceipt ?? {}), titleFound: true },
|
|
396
|
+
receipts,
|
|
397
|
+
publishedAt: now(),
|
|
398
|
+
};
|
|
399
|
+
} catch (error) {
|
|
400
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
401
|
+
if (schemaDriftMessage(message)) {
|
|
402
|
+
return {
|
|
403
|
+
status: 'page_schema_changed',
|
|
404
|
+
recipeVersion: RECIPE_VERSION,
|
|
405
|
+
checkpoint: currentStep,
|
|
406
|
+
message,
|
|
407
|
+
receipts,
|
|
408
|
+
next: 'Re-discover only the failed semantic anchor, update the Xiaohongshu recipe version, then resume with the same content inputs.',
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
throw error;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
function health(): AssistantPluginHealth {
|
|
416
|
+
return {
|
|
417
|
+
state: 'ready',
|
|
418
|
+
checkedAt: now(),
|
|
419
|
+
ready: true,
|
|
420
|
+
probed: true,
|
|
421
|
+
errors: [],
|
|
422
|
+
warnings: ['Xiaohongshu authentication is owned by the Browser profile; login validity is checked at publish preflight and no raw token/cookie is stored by this recipe.'],
|
|
423
|
+
details: { recipeVersion: RECIPE_VERSION, browserSessionRequired: true },
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
function permissions(): AssistantPluginPermissionScope[] {
|
|
428
|
+
return [
|
|
429
|
+
{ scope: 'xiaohongshu.recipe', mode: 'read', description: 'Resolve and classify the versioned Xiaohongshu publishing recipe.', granted: true, required: true },
|
|
430
|
+
{ scope: 'xiaohongshu.publish', mode: 'write', description: 'Execute the bounded Xiaohongshu publishing recipe through the existing Browser plugin.', granted: true, required: true },
|
|
431
|
+
];
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function capabilities(): AssistantPluginCapability[] {
|
|
435
|
+
return [{
|
|
436
|
+
capabilityId: 'xiaohongshu-publish',
|
|
437
|
+
title: 'Xiaohongshu Publishing Recipe',
|
|
438
|
+
description: 'Versioned Browser-backed publishing flow with auth fencing, image/long-text routing, generated-image handoff, semantic publish activation, and dual verification.',
|
|
439
|
+
scopes: ['xiaohongshu.recipe', 'xiaohongshu.publish'],
|
|
440
|
+
actions: ['get_publish_recipe', 'classify_publish_state', 'publish_note'],
|
|
441
|
+
}];
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function actions(): AssistantPluginActionDescriptor[] {
|
|
445
|
+
const recipeSchema = {
|
|
446
|
+
type: 'object',
|
|
447
|
+
properties: {
|
|
448
|
+
mode: { type: 'string', enum: ['image_note', 'generated_image_note', 'long_text'] },
|
|
449
|
+
session_id: { type: 'string' },
|
|
450
|
+
profile_url: { type: 'string' },
|
|
451
|
+
title: { type: 'string' },
|
|
452
|
+
body: { type: 'string' },
|
|
453
|
+
image_paths: { type: 'array', minItems: 1, maxItems: 18, items: { type: 'string' } },
|
|
454
|
+
summary: { type: 'string' },
|
|
455
|
+
template_text: { type: 'string' },
|
|
456
|
+
},
|
|
457
|
+
required: ['mode', 'session_id', 'profile_url', 'title', 'body'],
|
|
458
|
+
additionalProperties: false,
|
|
459
|
+
};
|
|
460
|
+
return [
|
|
461
|
+
{
|
|
462
|
+
actionId: 'get_publish_recipe', title: 'Resolve publish recipe', description: 'Return the current deterministic Xiaohongshu Browser action sequence without executing it.',
|
|
463
|
+
readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
|
|
464
|
+
scopes: ['xiaohongshu.recipe'], resourceClaims: [], argumentsSchema: recipeSchema,
|
|
465
|
+
},
|
|
466
|
+
{
|
|
467
|
+
actionId: 'classify_publish_state', title: 'Classify publish state', description: 'Classify observed Xiaohongshu URL/text as ready, auth-required, published receipt, profile verified, pending, or schema drift.',
|
|
468
|
+
readOnly: true, risk: 'readonly', confirmation: 'none', defaultTimeoutMs: 15_000, cancellable: true, idempotent: true,
|
|
469
|
+
scopes: ['xiaohongshu.recipe'], resourceClaims: [],
|
|
470
|
+
argumentsSchema: {
|
|
471
|
+
type: 'object',
|
|
472
|
+
properties: {
|
|
473
|
+
phase: { type: 'string', enum: ['preflight', 'creator_receipt', 'profile_verify'] },
|
|
474
|
+
url: { type: 'string' }, text: { type: 'string' }, expected_title: { type: 'string' },
|
|
475
|
+
},
|
|
476
|
+
required: ['phase', 'url', 'text'], additionalProperties: false,
|
|
477
|
+
},
|
|
478
|
+
},
|
|
479
|
+
{
|
|
480
|
+
actionId: 'publish_note', title: 'Publish Xiaohongshu note', description: 'Execute the versioned Xiaohongshu publish recipe through the existing persisted Browser session. Stops on login expiry or page-schema drift and only reports published after a Creator success receipt plus exact-title verification in Creator note manager.',
|
|
481
|
+
readOnly: false, risk: 'remote_write', confirmation: 'authorization', defaultTimeoutMs: 180_000, cancellable: true, idempotent: false,
|
|
482
|
+
scopes: ['xiaohongshu.publish'], resourceClaims: [{ resource: 'remote', mode: 'exclusive' }, { resource: 'repo-state', mode: 'write' }], argumentsSchema: recipeSchema,
|
|
483
|
+
},
|
|
484
|
+
];
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
export function buildXiaohongshuPluginManifest(previousRevision = 0, previousUpdatedAt?: string): AssistantPluginManifest {
|
|
488
|
+
return {
|
|
489
|
+
schemaVersion: 1,
|
|
490
|
+
manifestVersion: 1,
|
|
491
|
+
revision: Math.max(1, previousRevision || 1),
|
|
492
|
+
pluginId: PLUGIN_ID,
|
|
493
|
+
provider: 'xiaohongshu-web-recipe',
|
|
494
|
+
displayName: 'Xiaohongshu Publishing',
|
|
495
|
+
pluginVersion: '1.0.0',
|
|
496
|
+
authority: {
|
|
497
|
+
strategy: 'derived',
|
|
498
|
+
duplicateStateAllowed: false,
|
|
499
|
+
sourceOfTruth: ['source:src/runtime/plugins/xiaohongshu-publish.ts', 'browser-profile:authentication-and-session'],
|
|
500
|
+
},
|
|
501
|
+
enabled: true,
|
|
502
|
+
lifecycle: { state: 'enabled', reason: 'Versioned Xiaohongshu publishing recipe is available; live authentication is fenced at execution time.' },
|
|
503
|
+
health: health(),
|
|
504
|
+
permissions: permissions(),
|
|
505
|
+
capabilities: capabilities(),
|
|
506
|
+
actions: actions(),
|
|
507
|
+
updatedAt: previousUpdatedAt ?? now(),
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
export const xiaohongshuPluginAdapter = {
|
|
512
|
+
pluginId: PLUGIN_ID,
|
|
513
|
+
buildManifest: buildXiaohongshuPluginManifest,
|
|
514
|
+
executeAction: executeXiaohongshuPluginAction,
|
|
515
|
+
};
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { join } from 'path';
|
|
2
2
|
import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
|
|
3
|
-
import {
|
|
4
|
-
repositoryCheckoutLifecycle,
|
|
5
|
-
selectRepositoryCheckout,
|
|
6
|
-
} from '../../cli/repositories/registry';
|
|
3
|
+
import { selectRepositoryCheckout } from '../../cli/repositories/registry';
|
|
7
4
|
import {
|
|
8
5
|
repositoryGitStatus,
|
|
9
6
|
type RepositoryGitStatusSnapshot,
|
|
@@ -66,18 +63,21 @@ export function sampleRepositoryGitStatusForRepositories(
|
|
|
66
63
|
let sampled = 0;
|
|
67
64
|
const failed: Array<{ repoId: string; checkoutId: string; message: string }> = [];
|
|
68
65
|
for (const repository of repositories) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
66
|
+
try {
|
|
67
|
+
// Scheduler projections consume only the currently selected checkout.
|
|
68
|
+
// Historical/parallel checkouts can be refreshed explicitly when selected;
|
|
69
|
+
// sampling all of them turns stale registry history into Runtime hot-path work.
|
|
70
|
+
writeRepositoryGitStatusSample(
|
|
71
|
+
controllerHome,
|
|
72
|
+
selectRepositoryCheckout(repository, repository.activeCheckoutId),
|
|
73
|
+
);
|
|
74
|
+
sampled += 1;
|
|
75
|
+
} catch (error) {
|
|
76
|
+
failed.push({
|
|
77
|
+
repoId: repository.repoId,
|
|
78
|
+
checkoutId: repository.activeCheckoutId,
|
|
79
|
+
message: error instanceof Error ? error.message : String(error),
|
|
80
|
+
});
|
|
81
81
|
}
|
|
82
82
|
}
|
|
83
83
|
return { sampled, failed };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { join } from 'path';
|
|
3
3
|
import { listActiveExecutionJobs, listExecutionJobs } from '../execution/jobs/store';
|
|
4
|
-
import type
|
|
4
|
+
import { TERMINAL_JOB_STATUSES, type ExecutionJob } from '../execution/jobs/types';
|
|
5
5
|
import { listActiveLeases } from '../resources/leases/store';
|
|
6
6
|
import { readJsonFile, writeJsonAtomic } from '../shared/json-files';
|
|
7
7
|
import { repositoryControllerRoot } from '../../cli/repositories/controller-home';
|
|
@@ -27,6 +27,7 @@ import type { ProjectionObservation, ProjectionSourceReconciliation } from '../h
|
|
|
27
27
|
import { RUNTIME_HEALTH_THRESHOLDS } from '../health/evaluator';
|
|
28
28
|
import { isProcessAlive } from '../shared/process-tree';
|
|
29
29
|
import type { TaskLedgerProjection } from '../../cli/controller/task-ledger';
|
|
30
|
+
import { collectWorkLifecycleAttention } from '../control-plane/execution/work-lifecycle-audit';
|
|
30
31
|
|
|
31
32
|
export interface ProjectionMetadata {
|
|
32
33
|
contentRevision: number;
|
|
@@ -125,6 +126,15 @@ function attentionSummary(job: ExecutionJob): RepositoryRuntimeProjection['atten
|
|
|
125
126
|
return { jobId: job.jobId, status: job.status, message: job.error?.message };
|
|
126
127
|
}
|
|
127
128
|
|
|
129
|
+
function executionAttentionIsCurrent(job: ExecutionJob, activeJobIds: ReadonlySet<string>): boolean {
|
|
130
|
+
// Status is the durable lifecycle authority. Legacy/migrated terminal records may
|
|
131
|
+
// lack finishedAt, but that omission must not resurrect terminal attention as a
|
|
132
|
+
// current release blocker. If a future attention status is non-terminal, retain
|
|
133
|
+
// the legacy timestamp/index fallback for that genuinely active state.
|
|
134
|
+
if (TERMINAL_JOB_STATUSES.has(job.status)) return false;
|
|
135
|
+
return !job.finishedAt || activeJobIds.has(job.jobId);
|
|
136
|
+
}
|
|
137
|
+
|
|
128
138
|
function projectionVisibleLeases(leases: ReturnType<typeof listActiveLeases>) {
|
|
129
139
|
// Ephemeral leases protect fast-path Process execution from conflicting writes,
|
|
130
140
|
// but by contract they do not represent Scheduler/Worker durable activity.
|
|
@@ -155,9 +165,11 @@ function projectionWithExecutionIndexOverlay(
|
|
|
155
165
|
const attentionJobs = recentJobs?.filter((job) => ATTENTION_JOB_STATUSES.has(job.status));
|
|
156
166
|
const attention = attentionJobs?.map(attentionSummary) ?? base.attention;
|
|
157
167
|
const currentAttention = attentionJobs
|
|
158
|
-
?.filter((job) =>
|
|
168
|
+
?.filter((job) => executionAttentionIsCurrent(job, activeJobIds))
|
|
159
169
|
.map(attentionSummary)
|
|
160
170
|
?? base.currentAttention;
|
|
171
|
+
const repository = listRepositories(controllerHome, { includeRemoved: true }).find((entry) => entry.repoId === repoId);
|
|
172
|
+
const lifecycleAttention = repository ? collectWorkLifecycleAttention(controllerHome, repository) : [];
|
|
161
173
|
|
|
162
174
|
return {
|
|
163
175
|
...base,
|
|
@@ -172,8 +184,8 @@ function projectionWithExecutionIndexOverlay(
|
|
|
172
184
|
releaseFrozen: projectionLeases
|
|
173
185
|
? projectionLeases.some((lease) => lease.resourceKey.startsWith('release:'))
|
|
174
186
|
: base.releaseFrozen,
|
|
175
|
-
currentAttention,
|
|
176
|
-
attention,
|
|
187
|
+
currentAttention: [...currentAttention, ...lifecycleAttention].slice(0, 100),
|
|
188
|
+
attention: [...attention, ...lifecycleAttention].slice(0, 100),
|
|
177
189
|
};
|
|
178
190
|
}
|
|
179
191
|
|
|
@@ -196,8 +208,10 @@ function buildRepositoryProjection(
|
|
|
196
208
|
.filter((job) => ATTENTION_JOB_STATUSES.has(job.status));
|
|
197
209
|
// Terminal attention records remain in history for diagnosis and audit, but only
|
|
198
210
|
// active/unresolved records should influence "current readiness" decisions.
|
|
199
|
-
|
|
211
|
+
// Terminal status wins over a missing legacy finishedAt timestamp.
|
|
212
|
+
const currentAttentionJobs = attentionJobs.filter((job) => executionAttentionIsCurrent(job, activeJobIds));
|
|
200
213
|
const repository = listRepositories(controllerHome).find((entry) => entry.repoId === repoId);
|
|
214
|
+
const lifecycleAttention = repository ? collectWorkLifecycleAttention(controllerHome, repository) : [];
|
|
201
215
|
const plugins = repository ? listAssistantPluginManifests(controllerHome, repository, {
|
|
202
216
|
preferStored: true,
|
|
203
217
|
}) : [];
|
|
@@ -225,10 +239,14 @@ function buildRepositoryProjection(
|
|
|
225
239
|
queueDepth: activeJobs.filter((job) => job.status !== 'running' && job.status !== 'dispatched').length,
|
|
226
240
|
runningWorkers: activeJobs.filter((job) => job.status === 'running').length,
|
|
227
241
|
activeLeases: leases.length,
|
|
228
|
-
currentAttention:
|
|
229
|
-
.map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
|
|
230
|
-
|
|
231
|
-
|
|
242
|
+
currentAttention: [
|
|
243
|
+
...currentAttentionJobs.map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
|
|
244
|
+
...lifecycleAttention,
|
|
245
|
+
].slice(0, 100),
|
|
246
|
+
attention: [
|
|
247
|
+
...attentionJobs.map((job) => ({ jobId: job.jobId, status: job.status, message: job.error?.message })),
|
|
248
|
+
...lifecycleAttention,
|
|
249
|
+
].slice(0, 100),
|
|
232
250
|
plugins: {
|
|
233
251
|
total: plugins.length,
|
|
234
252
|
enabled: plugins.filter((plugin) => plugin.enabled).length,
|
|
@@ -620,7 +638,7 @@ export function readRepositoryProjectionSnapshot(
|
|
|
620
638
|
|
|
621
639
|
if (!stale && persisted) {
|
|
622
640
|
dirtyProjectionReadCache.delete(dirtyProjectionReadCacheKey(controllerHome, repoId));
|
|
623
|
-
return { projection: persisted, stale: false, persisted: true };
|
|
641
|
+
return { projection: projectionWithExecutionIndexOverlay(controllerHome, repoId, persisted), stale: false, persisted: true };
|
|
624
642
|
}
|
|
625
643
|
|
|
626
644
|
// A hot read must remain read-only and must not rebuild the full projection in
|
|
@@ -131,16 +131,6 @@ export const RECOVERY_ACTIONS = {
|
|
|
131
131
|
localOnly: true,
|
|
132
132
|
boundedTo: ['google_workspace_auth_handoff'],
|
|
133
133
|
},
|
|
134
|
-
browserDomainAccessPreview: {
|
|
135
|
-
id: 'recovery.browser_domain_access_preview',
|
|
136
|
-
title: 'Preview browser domain grant',
|
|
137
|
-
description: 'Preview a domain-key browser grant instead of retrying arbitrary URLs.',
|
|
138
|
-
class: 'browser_domain_grant_required',
|
|
139
|
-
risk: 'readonly',
|
|
140
|
-
confirmation: 'none',
|
|
141
|
-
localOnly: true,
|
|
142
|
-
boundedTo: ['browser_allowed_domains'],
|
|
143
|
-
},
|
|
144
134
|
externalFilesystemGrantPreview: {
|
|
145
135
|
id: 'recovery.external_filesystem_grant_preview',
|
|
146
136
|
title: 'Preview external filesystem grant',
|