@voybio/ace-swarm 0.1.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/CHANGELOG.md +109 -0
- package/LICENSE +186 -0
- package/README.md +229 -0
- package/assets/.agents/ACE/ACE-Init/AGENTS.md +210 -0
- package/assets/.agents/ACE/ACE-Init/instructions.md +118 -0
- package/assets/.agents/ACE/ACE_coders/AGENTS.md +154 -0
- package/assets/.agents/ACE/ACE_coders/INSTRUCTIONS.md +216 -0
- package/assets/.agents/ACE/AGENT_REGISTRY.md +70 -0
- package/assets/.agents/ACE/AGENT_REGISTRY_7.md +9 -0
- package/assets/.agents/ACE/DIRECTIVE_KERNEL.md +234 -0
- package/assets/.agents/ACE/UI/AGENTS.md +115 -0
- package/assets/.agents/ACE/UI/instructions.md +178 -0
- package/assets/.agents/ACE/VOS/ACE_VOS_MISSING_INFO_MATRIX.md +42 -0
- package/assets/.agents/ACE/VOS/AGENTS.md +72 -0
- package/assets/.agents/ACE/VOS/instructions.md +211 -0
- package/assets/.agents/ACE/agent-astgrep/AGENTS.md +123 -0
- package/assets/.agents/ACE/agent-astgrep/instructions.md +91 -0
- package/assets/.agents/ACE/agent-builder/AGENTS.md +172 -0
- package/assets/.agents/ACE/agent-builder/instructions.md +137 -0
- package/assets/.agents/ACE/agent-docs/AGENTS.md +159 -0
- package/assets/.agents/ACE/agent-docs/instructions.md +133 -0
- package/assets/.agents/ACE/agent-eval/AGENTS.md +46 -0
- package/assets/.agents/ACE/agent-eval/instructions.md +56 -0
- package/assets/.agents/ACE/agent-memory/AGENTS.md +49 -0
- package/assets/.agents/ACE/agent-memory/instructions.md +50 -0
- package/assets/.agents/ACE/agent-observability/AGENTS.md +46 -0
- package/assets/.agents/ACE/agent-observability/instructions.md +50 -0
- package/assets/.agents/ACE/agent-ops/AGENTS.md +201 -0
- package/assets/.agents/ACE/agent-ops/instructions.md +136 -0
- package/assets/.agents/ACE/agent-qa/AGENTS.md +189 -0
- package/assets/.agents/ACE/agent-qa/instructions.md +121 -0
- package/assets/.agents/ACE/agent-release/AGENTS.md +48 -0
- package/assets/.agents/ACE/agent-release/instructions.md +49 -0
- package/assets/.agents/ACE/agent-research/AGENTS.md +160 -0
- package/assets/.agents/ACE/agent-research/instructions.md +118 -0
- package/assets/.agents/ACE/agent-security/AGENTS.md +48 -0
- package/assets/.agents/ACE/agent-security/instructions.md +50 -0
- package/assets/.agents/ACE/agent-skeptic/AGENTS.md +178 -0
- package/assets/.agents/ACE/agent-skeptic/instructions.md +196 -0
- package/assets/.agents/ACE/agent-spec/AGENTS.md +169 -0
- package/assets/.agents/ACE/agent-spec/instructions.md +116 -0
- package/assets/.agents/ACE/orchestrator/AGENTS.md +365 -0
- package/assets/.agents/ACE/orchestrator/instructions.md +231 -0
- package/assets/.agents/skills/ace-orchestrator/SKILL.md +63 -0
- package/assets/.agents/skills/ace-orchestrator/references/engineering-bootstrap-playbook.md +360 -0
- package/assets/.agents/skills/astgrep-index/SKILL.md +58 -0
- package/assets/.agents/skills/codemunch/SKILL.md +65 -0
- package/assets/.agents/skills/codemunch/references/ast-driven-protocol.md +543 -0
- package/assets/.agents/skills/codesnipe/SKILL.md +64 -0
- package/assets/.agents/skills/codesnipe/references/dual-codebase-playbook.md +671 -0
- package/assets/.agents/skills/eval-harness/SKILL.md +203 -0
- package/assets/.agents/skills/handoff-lint/SKILL.md +164 -0
- package/assets/.agents/skills/incident-commander/SKILL.md +174 -0
- package/assets/.agents/skills/landing-review-watcher/SKILL.md +68 -0
- package/assets/.agents/skills/memory-curator/SKILL.md +179 -0
- package/assets/.agents/skills/problem-triage/SKILL.md +57 -0
- package/assets/.agents/skills/problem-triage/agents/openai.yaml +3 -0
- package/assets/.agents/skills/release-sentry/SKILL.md +189 -0
- package/assets/.agents/skills/risk-quant/SKILL.md +190 -0
- package/assets/.agents/skills/schema-forge/SKILL.md +174 -0
- package/assets/.agents/skills/skill-auditor/SKILL.md +52 -0
- package/assets/.agents/skills/state-auditor/SKILL.md +182 -0
- package/assets/.github/hooks/ace-copilot.json +68 -0
- package/assets/agent-state/ACE_WORKFLOW.md +131 -0
- package/assets/agent-state/ARTIFACT_MANIFEST.json +5 -0
- package/assets/agent-state/AST_GREP_COMMANDS.md +121 -0
- package/assets/agent-state/AST_GREP_INDEX.json +13 -0
- package/assets/agent-state/AST_GREP_INDEX.md +15 -0
- package/assets/agent-state/DECISIONS.md +7 -0
- package/assets/agent-state/EVIDENCE_LOG.md +7 -0
- package/assets/agent-state/HANDOFF.json +24 -0
- package/assets/agent-state/INTERFACE_REGISTRY.md +75 -0
- package/assets/agent-state/MODULES/gates/gate-autonomy.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-completeness.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-correctness.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-evaluation.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-operability.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-security.json +7 -0
- package/assets/agent-state/MODULES/gates/gate-typescript-public-surface.json +7 -0
- package/assets/agent-state/MODULES/registry.json +41 -0
- package/assets/agent-state/MODULES/roles/capability-astgrep.json +49 -0
- package/assets/agent-state/MODULES/roles/capability-build.json +39 -0
- package/assets/agent-state/MODULES/roles/capability-docs.json +38 -0
- package/assets/agent-state/MODULES/roles/capability-eval.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-memory.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-observability.json +20 -0
- package/assets/agent-state/MODULES/roles/capability-ops.json +45 -0
- package/assets/agent-state/MODULES/roles/capability-qa.json +40 -0
- package/assets/agent-state/MODULES/roles/capability-release.json +21 -0
- package/assets/agent-state/MODULES/roles/capability-research.json +44 -0
- package/assets/agent-state/MODULES/roles/capability-security.json +21 -0
- package/assets/agent-state/MODULES/roles/capability-skeptic.json +48 -0
- package/assets/agent-state/MODULES/roles/capability-spec.json +42 -0
- package/assets/agent-state/MODULES/schemas/ACE_RUNTIME_PROFILE.schema.json +289 -0
- package/assets/agent-state/MODULES/schemas/ARTIFACT_MANIFEST.schema.json +185 -0
- package/assets/agent-state/MODULES/schemas/HANDOFF.agent-state.schema.json +124 -0
- package/assets/agent-state/MODULES/schemas/HANDOFF.schema.json +55 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_EXECUTOR_SESSION_REGISTRY.schema.json +290 -0
- package/assets/agent-state/MODULES/schemas/RUNTIME_TOOL_SPEC_REGISTRY.schema.json +144 -0
- package/assets/agent-state/MODULES/schemas/STATUS_EVENT.schema.json +84 -0
- package/assets/agent-state/MODULES/schemas/SWARM_HANDOFF.schema.json +138 -0
- package/assets/agent-state/MODULES/schemas/TRACKER_SNAPSHOT.schema.json +134 -0
- package/assets/agent-state/MODULES/schemas/VERICIFY_BRIDGE_SNAPSHOT.schema.json +157 -0
- package/assets/agent-state/MODULES/schemas/VERICIFY_PROCESS_POST_LOG.schema.json +93 -0
- package/assets/agent-state/MODULES/schemas/WORKSPACE_SESSION_REGISTRY.schema.json +133 -0
- package/assets/agent-state/PROVENANCE_LOG.md +28 -0
- package/assets/agent-state/QUALITY_GATES.md +15 -0
- package/assets/agent-state/RISKS.md +8 -0
- package/assets/agent-state/SCOPE.md +20 -0
- package/assets/agent-state/SKILL_CATALOG.md +48 -0
- package/assets/agent-state/STATUS.md +8 -0
- package/assets/agent-state/STATUS_EVENTS.ndjson +1 -0
- package/assets/agent-state/TASK.md +18 -0
- package/assets/agent-state/TEAL_CONFIG.md +117 -0
- package/assets/agent-state/handoff-registry.json +5 -0
- package/assets/agent-state/index-fingerprints.json +7 -0
- package/assets/agent-state/index.json +32 -0
- package/assets/agent-state/run-ledger.json +5 -0
- package/assets/agent-state/runtime-executor-sessions.json +5 -0
- package/assets/agent-state/runtime-tool-specs.json +5 -0
- package/assets/agent-state/runtime-workspaces.json +5 -0
- package/assets/agent-state/todo-state.json +7 -0
- package/assets/agent-state/tracker-snapshot.json +7 -0
- package/assets/agent-state/vericify/ace-bridge.json +60 -0
- package/assets/agent-state/vericify/process-posts.json +5 -0
- package/assets/instructions/ACE.instructions.md +187 -0
- package/assets/instructions/ACE_Coder.instructions.md +146 -0
- package/assets/instructions/ACE_UI.instructions.md +178 -0
- package/assets/instructions/ACE_VOS.instructions.md +211 -0
- package/assets/scripts/ace-hook-dispatch.mjs +538 -0
- package/assets/scripts/bootstrap-workspace.sh +27 -0
- package/assets/scripts/copilot-hook-dispatch.mjs +3 -0
- package/assets/scripts/eval-harness.sh +68 -0
- package/assets/scripts/render-mcp-configs.sh +396 -0
- package/assets/tasks/README.md +48 -0
- package/assets/tasks/SWARM_HANDOFF.example.json +53 -0
- package/assets/tasks/SWARM_HANDOFF.example_ui_to_coders.json +55 -0
- package/assets/tasks/SWARM_HANDOFF.example_vos_to_ui.json +55 -0
- package/assets/tasks/SWARM_HANDOFF.template.json +52 -0
- package/assets/tasks/cli_work_split.md +22 -0
- package/assets/tasks/lessons.md +17 -0
- package/assets/tasks/role_tasks.md +206 -0
- package/assets/tasks/todo.md +23 -0
- package/dist/ace-autonomy.d.ts +137 -0
- package/dist/ace-autonomy.js +472 -0
- package/dist/ace-context.d.ts +29 -0
- package/dist/ace-context.js +240 -0
- package/dist/ace-internal-tools.d.ts +8 -0
- package/dist/ace-internal-tools.js +76 -0
- package/dist/ace-server-instructions.d.ts +12 -0
- package/dist/ace-server-instructions.js +324 -0
- package/dist/agent-runtime/role-adapters.d.ts +29 -0
- package/dist/agent-runtime/role-adapters.js +573 -0
- package/dist/astgrep-index.d.ts +24 -0
- package/dist/astgrep-index.js +476 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +591 -0
- package/dist/git-ops.d.ts +53 -0
- package/dist/git-ops.js +238 -0
- package/dist/handoff-registry.d.ts +71 -0
- package/dist/handoff-registry.js +422 -0
- package/dist/helpers.d.ts +126 -0
- package/dist/helpers.js +1687 -0
- package/dist/index-store.d.ts +51 -0
- package/dist/index-store.js +328 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +7 -0
- package/dist/internal-tool-runtime.d.ts +21 -0
- package/dist/internal-tool-runtime.js +136 -0
- package/dist/job-scheduler.d.ts +175 -0
- package/dist/job-scheduler.js +1217 -0
- package/dist/kanban.d.ts +27 -0
- package/dist/kanban.js +339 -0
- package/dist/local-model-runtime.d.ts +40 -0
- package/dist/local-model-runtime.js +174 -0
- package/dist/model-bridge.d.ts +54 -0
- package/dist/model-bridge.js +587 -0
- package/dist/orchestrator-supervisor.d.ts +100 -0
- package/dist/orchestrator-supervisor.js +399 -0
- package/dist/problem-triage.d.ts +23 -0
- package/dist/problem-triage.js +448 -0
- package/dist/prompts.d.ts +7 -0
- package/dist/prompts.js +628 -0
- package/dist/public-surface.d.ts +30 -0
- package/dist/public-surface.js +316 -0
- package/dist/resources.d.ts +7 -0
- package/dist/resources.js +545 -0
- package/dist/run-ledger.d.ts +36 -0
- package/dist/run-ledger.js +257 -0
- package/dist/runtime-command.d.ts +18 -0
- package/dist/runtime-command.js +76 -0
- package/dist/runtime-executor.d.ts +104 -0
- package/dist/runtime-executor.js +985 -0
- package/dist/runtime-profile.d.ts +116 -0
- package/dist/runtime-profile.js +532 -0
- package/dist/runtime-tool-specs.d.ts +68 -0
- package/dist/runtime-tool-specs.js +527 -0
- package/dist/safe-edit.d.ts +52 -0
- package/dist/safe-edit.js +255 -0
- package/dist/schemas.d.ts +44 -0
- package/dist/schemas.js +830 -0
- package/dist/semantic-cache.d.ts +147 -0
- package/dist/semantic-cache.js +552 -0
- package/dist/semantic-hash.d.ts +83 -0
- package/dist/semantic-hash.js +346 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +46 -0
- package/dist/shared.d.ts +136 -0
- package/dist/shared.js +269 -0
- package/dist/skill-auditor.d.ts +26 -0
- package/dist/skill-auditor.js +184 -0
- package/dist/skill-catalog.d.ts +60 -0
- package/dist/skill-catalog.js +305 -0
- package/dist/status-events.d.ts +40 -0
- package/dist/status-events.js +269 -0
- package/dist/store/ace-packed-store.d.ts +69 -0
- package/dist/store/ace-packed-store.js +434 -0
- package/dist/store/bootstrap-store.d.ts +46 -0
- package/dist/store/bootstrap-store.js +242 -0
- package/dist/store/catalog-builder.d.ts +21 -0
- package/dist/store/catalog-builder.js +68 -0
- package/dist/store/importer.d.ts +19 -0
- package/dist/store/importer.js +157 -0
- package/dist/store/knowledge-bake.d.ts +59 -0
- package/dist/store/knowledge-bake.js +339 -0
- package/dist/store/materializers/hook-context-materializer.d.ts +25 -0
- package/dist/store/materializers/hook-context-materializer.js +100 -0
- package/dist/store/materializers/host-file-materializer.d.ts +37 -0
- package/dist/store/materializers/host-file-materializer.js +271 -0
- package/dist/store/materializers/todo-syncer.d.ts +30 -0
- package/dist/store/materializers/todo-syncer.js +140 -0
- package/dist/store/materializers/vericify-projector.d.ts +38 -0
- package/dist/store/materializers/vericify-projector.js +239 -0
- package/dist/store/repositories/discovery-repository.d.ts +24 -0
- package/dist/store/repositories/discovery-repository.js +58 -0
- package/dist/store/repositories/handoff-repository.d.ts +31 -0
- package/dist/store/repositories/handoff-repository.js +67 -0
- package/dist/store/repositories/ledger-repository.d.ts +26 -0
- package/dist/store/repositories/ledger-repository.js +49 -0
- package/dist/store/repositories/runtime-kv-repository.d.ts +16 -0
- package/dist/store/repositories/runtime-kv-repository.js +36 -0
- package/dist/store/repositories/scheduler-repository.d.ts +50 -0
- package/dist/store/repositories/scheduler-repository.js +123 -0
- package/dist/store/repositories/session-repository.d.ts +33 -0
- package/dist/store/repositories/session-repository.js +82 -0
- package/dist/store/repositories/todo-repository.d.ts +31 -0
- package/dist/store/repositories/todo-repository.js +77 -0
- package/dist/store/repositories/tracker-repository.d.ts +25 -0
- package/dist/store/repositories/tracker-repository.js +43 -0
- package/dist/store/repositories/vericify-repository.d.ts +32 -0
- package/dist/store/repositories/vericify-repository.js +58 -0
- package/dist/store/skills-install.d.ts +28 -0
- package/dist/store/skills-install.js +86 -0
- package/dist/store/state-reader.d.ts +49 -0
- package/dist/store/state-reader.js +111 -0
- package/dist/store/store-artifacts.d.ts +12 -0
- package/dist/store/store-artifacts.js +138 -0
- package/dist/store/store-snapshot.d.ts +19 -0
- package/dist/store/store-snapshot.js +140 -0
- package/dist/store/topology-bake.d.ts +15 -0
- package/dist/store/topology-bake.js +215 -0
- package/dist/store/types.d.ts +155 -0
- package/dist/store/types.js +35 -0
- package/dist/store/workspace-snapshot.d.ts +26 -0
- package/dist/store/workspace-snapshot.js +107 -0
- package/dist/store/write-queue.d.ts +7 -0
- package/dist/store/write-queue.js +26 -0
- package/dist/todo-state.d.ts +41 -0
- package/dist/todo-state.js +399 -0
- package/dist/tools-agent.d.ts +7 -0
- package/dist/tools-agent.js +1542 -0
- package/dist/tools-discovery.d.ts +6 -0
- package/dist/tools-discovery.js +178 -0
- package/dist/tools-drift.d.ts +13 -0
- package/dist/tools-drift.js +357 -0
- package/dist/tools-files.d.ts +6 -0
- package/dist/tools-files.js +679 -0
- package/dist/tools-framework.d.ts +7 -0
- package/dist/tools-framework.js +1414 -0
- package/dist/tools-git.d.ts +6 -0
- package/dist/tools-git.js +183 -0
- package/dist/tools-handoff.d.ts +32 -0
- package/dist/tools-handoff.js +489 -0
- package/dist/tools-lifecycle.d.ts +6 -0
- package/dist/tools-lifecycle.js +205 -0
- package/dist/tools-memory.d.ts +6 -0
- package/dist/tools-memory.js +260 -0
- package/dist/tools-scheduler.d.ts +6 -0
- package/dist/tools-scheduler.js +228 -0
- package/dist/tools-skills.d.ts +3 -0
- package/dist/tools-skills.js +104 -0
- package/dist/tools-todo.d.ts +6 -0
- package/dist/tools-todo.js +154 -0
- package/dist/tools.d.ts +9 -0
- package/dist/tools.js +33 -0
- package/dist/tracker-adapters.d.ts +74 -0
- package/dist/tracker-adapters.js +776 -0
- package/dist/tracker-sync.d.ts +10 -0
- package/dist/tracker-sync.js +84 -0
- package/dist/tui/agent-runner.d.ts +137 -0
- package/dist/tui/agent-runner.js +466 -0
- package/dist/tui/agent-worker.d.ts +10 -0
- package/dist/tui/agent-worker.js +347 -0
- package/dist/tui/chat.d.ts +84 -0
- package/dist/tui/chat.js +368 -0
- package/dist/tui/commands.d.ts +57 -0
- package/dist/tui/commands.js +432 -0
- package/dist/tui/dashboard.d.ts +24 -0
- package/dist/tui/dashboard.js +110 -0
- package/dist/tui/index.d.ts +114 -0
- package/dist/tui/index.js +1059 -0
- package/dist/tui/input.d.ts +49 -0
- package/dist/tui/input.js +336 -0
- package/dist/tui/layout.d.ts +116 -0
- package/dist/tui/layout.js +367 -0
- package/dist/tui/ollama.d.ts +116 -0
- package/dist/tui/ollama.js +192 -0
- package/dist/tui/openai-compatible.d.ts +63 -0
- package/dist/tui/openai-compatible.js +370 -0
- package/dist/tui/provider-discovery.d.ts +59 -0
- package/dist/tui/provider-discovery.js +530 -0
- package/dist/tui/renderer.d.ts +166 -0
- package/dist/tui/renderer.js +304 -0
- package/dist/tui/tabs.d.ts +70 -0
- package/dist/tui/tabs.js +208 -0
- package/dist/tui/telemetry.d.ts +56 -0
- package/dist/tui/telemetry.js +106 -0
- package/dist/vericify-bridge.d.ts +146 -0
- package/dist/vericify-bridge.js +571 -0
- package/dist/vericify-context.d.ts +10 -0
- package/dist/vericify-context.js +72 -0
- package/dist/workspace-manager.d.ts +107 -0
- package/dist/workspace-manager.js +636 -0
- package/package.json +83 -0
|
@@ -0,0 +1,636 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { randomUUID } from "node:crypto";
|
|
3
|
+
import { existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, realpathSync, renameSync, rmSync, } from "node:fs";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { basename, dirname, join, relative, resolve } from "node:path";
|
|
6
|
+
import { resolveWorkspaceArtifactPath as resolveWorkspaceArtifactPathHelper, safeRead, safeWrite, resolveWorkspaceRoot, } from "./helpers.js";
|
|
7
|
+
import { storeExistsSync } from "./store/store-snapshot.js";
|
|
8
|
+
import { operationalArtifactVirtualPath, writeStoreBlobsSync, } from "./store/store-artifacts.js";
|
|
9
|
+
import { getRuntimeProfilePath, readRuntimeProfile } from "./runtime-profile.js";
|
|
10
|
+
import { validateWorkspaceSessionRegistryPayload } from "./schemas.js";
|
|
11
|
+
import { isInside, isReadError, slugify } from "./shared.js";
|
|
12
|
+
import { appendStatusEvent } from "./status-events.js";
|
|
13
|
+
import { buildAutonomyHookEnv, normalizeAutonomyPolicy, normalizeContinuityPolicy, } from "./ace-autonomy.js";
|
|
14
|
+
export const WORKSPACE_SESSION_REGISTRY_REL_PATH = "agent-state/runtime-workspaces.json";
|
|
15
|
+
const DEFAULT_MANAGED_WORKSPACE_ROOT = ".agents/ACE/.ace/workspaces";
|
|
16
|
+
const DEFAULT_HOOK_TIMEOUT_MS = 15_000;
|
|
17
|
+
const MAX_HOOK_TIMEOUT_MS = 60_000;
|
|
18
|
+
const MIN_HOOK_TIMEOUT_MS = 1_000;
|
|
19
|
+
function defaultHookState() {
|
|
20
|
+
return { status: "not_run" };
|
|
21
|
+
}
|
|
22
|
+
function defaultHookSummary() {
|
|
23
|
+
return {
|
|
24
|
+
after_create: defaultHookState(),
|
|
25
|
+
before_run: defaultHookState(),
|
|
26
|
+
after_run: defaultHookState(),
|
|
27
|
+
before_remove: defaultHookState(),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function defaultRegistry() {
|
|
31
|
+
return {
|
|
32
|
+
version: 1,
|
|
33
|
+
updated_at: "1970-01-01T00:00:00.000Z",
|
|
34
|
+
sessions: [],
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function clampHookTimeout(input) {
|
|
38
|
+
if (!Number.isFinite(input))
|
|
39
|
+
return DEFAULT_HOOK_TIMEOUT_MS;
|
|
40
|
+
return Math.max(MIN_HOOK_TIMEOUT_MS, Math.min(MAX_HOOK_TIMEOUT_MS, Math.trunc(input)));
|
|
41
|
+
}
|
|
42
|
+
function emitWorkspaceEvent(eventType, summary, payload) {
|
|
43
|
+
try {
|
|
44
|
+
appendStatusEvent({
|
|
45
|
+
source_module: "capability-framework",
|
|
46
|
+
event_type: eventType,
|
|
47
|
+
status: eventType === "WORKSPACE_SESSION_CREATED" ||
|
|
48
|
+
eventType === "WORKSPACE_SESSION_REMOVED"
|
|
49
|
+
? "done"
|
|
50
|
+
: "fail",
|
|
51
|
+
summary,
|
|
52
|
+
payload,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// Workspace lifecycle must not fail because status logging failed.
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function resolveRuntimeWorkspaceRoot(rootOverride) {
|
|
60
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
61
|
+
const configured = rootOverride?.trim() ||
|
|
62
|
+
readRuntimeProfile().workspace.root ||
|
|
63
|
+
DEFAULT_MANAGED_WORKSPACE_ROOT;
|
|
64
|
+
return resolve(configured.startsWith("/") ? configured : resolve(workspaceRoot, configured));
|
|
65
|
+
}
|
|
66
|
+
function resolveTargetPath(rootPath, target) {
|
|
67
|
+
return resolve(target.startsWith("/") ? target : resolve(rootPath, target));
|
|
68
|
+
}
|
|
69
|
+
function canonicalizeCandidatePath(targetPath) {
|
|
70
|
+
const remainder = [];
|
|
71
|
+
let cursor = resolve(targetPath);
|
|
72
|
+
while (!existsSync(cursor)) {
|
|
73
|
+
const parent = dirname(cursor);
|
|
74
|
+
if (parent === cursor)
|
|
75
|
+
break;
|
|
76
|
+
remainder.unshift(basename(cursor));
|
|
77
|
+
cursor = parent;
|
|
78
|
+
}
|
|
79
|
+
const existing = existsSync(cursor) ? realpathSync(cursor) : cursor;
|
|
80
|
+
return resolve(existing, ...remainder);
|
|
81
|
+
}
|
|
82
|
+
function parseWorkspaceRegistry(raw) {
|
|
83
|
+
if (isReadError(raw))
|
|
84
|
+
return defaultRegistry();
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(raw);
|
|
88
|
+
}
|
|
89
|
+
catch (error) {
|
|
90
|
+
throw new Error(`Workspace session registry contains invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
91
|
+
}
|
|
92
|
+
const validation = validateWorkspaceSessionRegistryPayload(parsed);
|
|
93
|
+
if (!validation.ok) {
|
|
94
|
+
throw new Error(`Workspace session registry failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
|
|
95
|
+
}
|
|
96
|
+
return parsed;
|
|
97
|
+
}
|
|
98
|
+
function resolveWorkspaceArtifactPath(filePath) {
|
|
99
|
+
return resolveWorkspaceArtifactPathHelper(filePath, "write");
|
|
100
|
+
}
|
|
101
|
+
function safeReadWorkspaceFile(filePath) {
|
|
102
|
+
return safeRead(filePath);
|
|
103
|
+
}
|
|
104
|
+
function safeWriteWorkspaceFile(filePath, content) {
|
|
105
|
+
return safeWrite(filePath, content);
|
|
106
|
+
}
|
|
107
|
+
function writeWorkspaceRegistry(registry) {
|
|
108
|
+
const validation = validateWorkspaceSessionRegistryPayload(registry);
|
|
109
|
+
if (!validation.ok) {
|
|
110
|
+
throw new Error(`Workspace session registry failed validation (${validation.schema}): ${validation.errors.join("; ")}`);
|
|
111
|
+
}
|
|
112
|
+
return safeWriteWorkspaceFile(WORKSPACE_SESSION_REGISTRY_REL_PATH, JSON.stringify(registry, null, 2));
|
|
113
|
+
}
|
|
114
|
+
function findSessionIndex(registry, input) {
|
|
115
|
+
if (input.session_id) {
|
|
116
|
+
return registry.sessions.findIndex((session) => session.session_id === input.session_id);
|
|
117
|
+
}
|
|
118
|
+
if (input.workspace_path) {
|
|
119
|
+
const expected = resolve(input.workspace_path);
|
|
120
|
+
return registry.sessions.findIndex((session) => resolve(session.workspace_path) === expected);
|
|
121
|
+
}
|
|
122
|
+
return -1;
|
|
123
|
+
}
|
|
124
|
+
function deriveWorkspacePath(rootPath, input, sessionId) {
|
|
125
|
+
if (input.workspace_path) {
|
|
126
|
+
return resolveTargetPath(rootPath, input.workspace_path);
|
|
127
|
+
}
|
|
128
|
+
const fallback = `session-${sessionId.slice(0, 8)}`;
|
|
129
|
+
const slug = slugify(input.workspace_name ?? input.objective_id ?? fallback) || fallback;
|
|
130
|
+
return resolve(rootPath, slug);
|
|
131
|
+
}
|
|
132
|
+
function workspaceHookStateFromResult(result) {
|
|
133
|
+
return result.detail ? { status: result.status, detail: result.detail } : { status: result.status };
|
|
134
|
+
}
|
|
135
|
+
function readWorkspaceRegistry() {
|
|
136
|
+
return parseWorkspaceRegistry(safeReadWorkspaceFile(WORKSPACE_SESSION_REGISTRY_REL_PATH));
|
|
137
|
+
}
|
|
138
|
+
export function getWorkspaceSessionRegistryPath() {
|
|
139
|
+
if (storeExistsSync(resolveWorkspaceRoot())) {
|
|
140
|
+
return operationalArtifactVirtualPath(resolveWorkspaceRoot(), WORKSPACE_SESSION_REGISTRY_REL_PATH);
|
|
141
|
+
}
|
|
142
|
+
return resolveWorkspaceArtifactPath(WORKSPACE_SESSION_REGISTRY_REL_PATH);
|
|
143
|
+
}
|
|
144
|
+
export function listWorkspaceSessions() {
|
|
145
|
+
return readWorkspaceRegistry();
|
|
146
|
+
}
|
|
147
|
+
export function validateManagedWorkspacePath(root, target, options = {}) {
|
|
148
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
149
|
+
const canonicalWorkspaceRoot = canonicalizeCandidatePath(workspaceRoot);
|
|
150
|
+
const rootPath = resolve(root.startsWith("/") ? root : resolve(workspaceRoot, root));
|
|
151
|
+
const targetPath = resolveTargetPath(rootPath, target);
|
|
152
|
+
if (rootPath === targetPath) {
|
|
153
|
+
return {
|
|
154
|
+
ok: false,
|
|
155
|
+
code: "root_path_not_session",
|
|
156
|
+
reason: "The configured root itself is never a valid managed workspace path.",
|
|
157
|
+
root_path: rootPath,
|
|
158
|
+
target_path: targetPath,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
if (!isInside(rootPath, targetPath)) {
|
|
162
|
+
return {
|
|
163
|
+
ok: false,
|
|
164
|
+
code: "outside_root",
|
|
165
|
+
reason: "Target path escapes the configured managed workspace root.",
|
|
166
|
+
root_path: rootPath,
|
|
167
|
+
target_path: targetPath,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const canonicalRootPath = canonicalizeCandidatePath(rootPath);
|
|
171
|
+
if (!options.allowExternalRoot && !isInside(canonicalWorkspaceRoot, canonicalRootPath)) {
|
|
172
|
+
return {
|
|
173
|
+
ok: false,
|
|
174
|
+
code: "root_outside_workspace",
|
|
175
|
+
reason: "Managed workspace root must resolve inside the ACE workspace root.",
|
|
176
|
+
root_path: rootPath,
|
|
177
|
+
target_path: targetPath,
|
|
178
|
+
canonical_root_path: canonicalRootPath,
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
const canonicalTargetPath = canonicalizeCandidatePath(targetPath);
|
|
182
|
+
if (!options.allowExternalRoot && !isInside(canonicalWorkspaceRoot, canonicalTargetPath)) {
|
|
183
|
+
return {
|
|
184
|
+
ok: false,
|
|
185
|
+
code: "path_outside_workspace",
|
|
186
|
+
reason: "Managed workspace target must resolve inside the ACE workspace root.",
|
|
187
|
+
root_path: rootPath,
|
|
188
|
+
target_path: targetPath,
|
|
189
|
+
canonical_root_path: canonicalRootPath,
|
|
190
|
+
canonical_target_path: canonicalTargetPath,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
if (canonicalRootPath === canonicalTargetPath ||
|
|
194
|
+
!isInside(canonicalRootPath, canonicalTargetPath)) {
|
|
195
|
+
return {
|
|
196
|
+
ok: false,
|
|
197
|
+
code: "symlink_escape",
|
|
198
|
+
reason: "Target path escapes the managed root after canonicalization (likely via symlink traversal).",
|
|
199
|
+
root_path: rootPath,
|
|
200
|
+
target_path: targetPath,
|
|
201
|
+
canonical_root_path: canonicalRootPath,
|
|
202
|
+
canonical_target_path: canonicalTargetPath,
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
return {
|
|
206
|
+
ok: true,
|
|
207
|
+
code: "ok",
|
|
208
|
+
root_path: rootPath,
|
|
209
|
+
target_path: targetPath,
|
|
210
|
+
canonical_root_path: canonicalRootPath,
|
|
211
|
+
canonical_target_path: canonicalTargetPath,
|
|
212
|
+
};
|
|
213
|
+
}
|
|
214
|
+
function shouldUseEphemeralWorkspaceRoot(input) {
|
|
215
|
+
return (input.source === "executor" &&
|
|
216
|
+
!input.root &&
|
|
217
|
+
!input.workspace_path &&
|
|
218
|
+
storeExistsSync(resolveWorkspaceRoot()));
|
|
219
|
+
}
|
|
220
|
+
function normalizeStorePathFragment(value) {
|
|
221
|
+
return value.split("\\").join("/");
|
|
222
|
+
}
|
|
223
|
+
function tryDecodeUtf8(bytes) {
|
|
224
|
+
try {
|
|
225
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
226
|
+
return {
|
|
227
|
+
encoding: "utf8",
|
|
228
|
+
content: decoder.decode(bytes),
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return {
|
|
233
|
+
encoding: "base64",
|
|
234
|
+
content: bytes.toString("base64"),
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
function listWorkspaceFiles(rootPath) {
|
|
239
|
+
if (!existsSync(rootPath))
|
|
240
|
+
return [];
|
|
241
|
+
const entries = [];
|
|
242
|
+
const walk = (cursor) => {
|
|
243
|
+
for (const item of readdirSync(cursor, { withFileTypes: true })) {
|
|
244
|
+
const fullPath = resolve(cursor, item.name);
|
|
245
|
+
if (item.isDirectory()) {
|
|
246
|
+
walk(fullPath);
|
|
247
|
+
}
|
|
248
|
+
else if (item.isFile()) {
|
|
249
|
+
entries.push(fullPath);
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
};
|
|
253
|
+
walk(rootPath);
|
|
254
|
+
return entries.sort((a, b) => a.localeCompare(b));
|
|
255
|
+
}
|
|
256
|
+
function captureWorkspaceToStore(session) {
|
|
257
|
+
const workspaceRoot = resolveWorkspaceRoot();
|
|
258
|
+
if (!storeExistsSync(workspaceRoot) || !existsSync(session.workspace_path)) {
|
|
259
|
+
return undefined;
|
|
260
|
+
}
|
|
261
|
+
const baseKey = `state/runtime/workspaces/sessions/${session.session_id}`;
|
|
262
|
+
const files = listWorkspaceFiles(session.workspace_path);
|
|
263
|
+
const manifestFiles = [];
|
|
264
|
+
const blobs = [];
|
|
265
|
+
for (const filePath of files) {
|
|
266
|
+
const relPath = normalizeStorePathFragment(relative(session.workspace_path, filePath));
|
|
267
|
+
const key = `${baseKey}/files/${relPath}`;
|
|
268
|
+
const raw = readFileSync(filePath);
|
|
269
|
+
const decoded = tryDecodeUtf8(raw);
|
|
270
|
+
blobs.push({ key, content: decoded.content });
|
|
271
|
+
manifestFiles.push({
|
|
272
|
+
path: relPath,
|
|
273
|
+
key,
|
|
274
|
+
encoding: decoded.encoding,
|
|
275
|
+
size_bytes: raw.length,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
const manifestKey = `${baseKey}/manifest.json`;
|
|
279
|
+
const manifest = {
|
|
280
|
+
version: 1,
|
|
281
|
+
captured_at: new Date().toISOString(),
|
|
282
|
+
session_id: session.session_id,
|
|
283
|
+
source: session.source,
|
|
284
|
+
status: session.status,
|
|
285
|
+
original_workspace_path: session.workspace_path,
|
|
286
|
+
original_root_path: session.root_path,
|
|
287
|
+
files: manifestFiles,
|
|
288
|
+
};
|
|
289
|
+
blobs.push({ key: manifestKey, content: JSON.stringify(manifest, null, 2) });
|
|
290
|
+
const written = writeStoreBlobsSync(workspaceRoot, blobs);
|
|
291
|
+
const storePath = (written[0] ?? "").split("#")[0];
|
|
292
|
+
return {
|
|
293
|
+
workspace_path: `${storePath}#${baseKey}`,
|
|
294
|
+
root_path: `${storePath}#${baseKey}`,
|
|
295
|
+
manifest_path: written[written.length - 1] ??
|
|
296
|
+
`${storePath}#${manifestKey}`,
|
|
297
|
+
file_count: manifestFiles.length,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
export function runWorkspaceHook(kind, session, timeoutMs) {
|
|
301
|
+
const runtimeProfile = readRuntimeProfile();
|
|
302
|
+
const command = runtimeProfile.workspace.hooks[kind];
|
|
303
|
+
if (!command) {
|
|
304
|
+
return {
|
|
305
|
+
ok: true,
|
|
306
|
+
kind,
|
|
307
|
+
status: "not_run",
|
|
308
|
+
command: null,
|
|
309
|
+
detail: "Hook not configured.",
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
const timeout = clampHookTimeout(timeoutMs);
|
|
313
|
+
const hookCwd = existsSync(session.workspace_path)
|
|
314
|
+
? session.workspace_path
|
|
315
|
+
: session.root_path;
|
|
316
|
+
const result = spawnSync("sh", ["-c", command], {
|
|
317
|
+
cwd: hookCwd,
|
|
318
|
+
encoding: "utf-8",
|
|
319
|
+
timeout,
|
|
320
|
+
maxBuffer: 4 * 1024 * 1024,
|
|
321
|
+
env: {
|
|
322
|
+
...process.env,
|
|
323
|
+
ACE_RUNTIME_PROFILE_PATH: getRuntimeProfilePath(),
|
|
324
|
+
ACE_WORKSPACE_SESSION_ID: session.session_id,
|
|
325
|
+
ACE_WORKSPACE_SESSION_PATH: session.workspace_path,
|
|
326
|
+
ACE_WORKSPACE_SESSION_ROOT: session.root_path,
|
|
327
|
+
ACE_WORKSPACE_HOOK_KIND: kind,
|
|
328
|
+
...buildAutonomyHookEnv(normalizeAutonomyPolicy(runtimeProfile.autonomy), normalizeContinuityPolicy(runtimeProfile.continuity)),
|
|
329
|
+
},
|
|
330
|
+
});
|
|
331
|
+
const output = ((result.stdout ?? "") + (result.stderr ?? "")).trim().slice(0, 4000);
|
|
332
|
+
const timedOut = result.signal === "SIGTERM" && result.status === null;
|
|
333
|
+
if (result.error || timedOut || result.status !== 0) {
|
|
334
|
+
return {
|
|
335
|
+
ok: false,
|
|
336
|
+
kind,
|
|
337
|
+
status: "failed",
|
|
338
|
+
command,
|
|
339
|
+
exit_code: result.status,
|
|
340
|
+
timed_out: timedOut,
|
|
341
|
+
detail: output ||
|
|
342
|
+
result.error?.message ||
|
|
343
|
+
`Hook exited with status ${result.status ?? "unknown"}`,
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
return {
|
|
347
|
+
ok: true,
|
|
348
|
+
kind,
|
|
349
|
+
status: "ok",
|
|
350
|
+
command,
|
|
351
|
+
exit_code: result.status,
|
|
352
|
+
detail: output || "Hook completed successfully.",
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
export function createWorkspaceSession(input) {
|
|
356
|
+
const registryPath = getWorkspaceSessionRegistryPath();
|
|
357
|
+
const sessionId = input.session_id?.trim() || randomUUID();
|
|
358
|
+
const now = new Date().toISOString();
|
|
359
|
+
let registry;
|
|
360
|
+
try {
|
|
361
|
+
registry = readWorkspaceRegistry();
|
|
362
|
+
}
|
|
363
|
+
catch (error) {
|
|
364
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
365
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", "Workspace session create failed because the registry could not be read.", { error: message });
|
|
366
|
+
return { ok: false, registry_path: registryPath, error: message };
|
|
367
|
+
}
|
|
368
|
+
const useEphemeralRoot = shouldUseEphemeralWorkspaceRoot(input);
|
|
369
|
+
const rootPath = useEphemeralRoot
|
|
370
|
+
? mkdtempSync(join(tmpdir(), "ace-runtime-workspaces-"))
|
|
371
|
+
: resolveRuntimeWorkspaceRoot(input.root);
|
|
372
|
+
const workspacePath = deriveWorkspacePath(rootPath, input, sessionId);
|
|
373
|
+
const validation = validateManagedWorkspacePath(rootPath, workspacePath, {
|
|
374
|
+
allowExternalRoot: useEphemeralRoot,
|
|
375
|
+
});
|
|
376
|
+
if (!validation.ok) {
|
|
377
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", `Workspace session create rejected for ${workspacePath}`, { validation });
|
|
378
|
+
return {
|
|
379
|
+
ok: false,
|
|
380
|
+
registry_path: registryPath,
|
|
381
|
+
error: validation.reason,
|
|
382
|
+
validation,
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
const duplicate = registry.sessions.find((session) => resolve(session.workspace_path) === validation.target_path &&
|
|
386
|
+
session.status !== "archived" &&
|
|
387
|
+
session.status !== "removed");
|
|
388
|
+
if (duplicate) {
|
|
389
|
+
const error = `Workspace path is already managed by session ${duplicate.session_id}.`;
|
|
390
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", error, { session_id: duplicate.session_id, workspace_path: duplicate.workspace_path });
|
|
391
|
+
return { ok: false, registry_path: registryPath, error, validation };
|
|
392
|
+
}
|
|
393
|
+
if (existsSync(validation.target_path)) {
|
|
394
|
+
const error = `Workspace path already exists and cannot be claimed: ${validation.target_path}`;
|
|
395
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", error, {
|
|
396
|
+
workspace_path: validation.target_path,
|
|
397
|
+
});
|
|
398
|
+
return { ok: false, registry_path: registryPath, error, validation };
|
|
399
|
+
}
|
|
400
|
+
mkdirSync(validation.root_path, { recursive: true });
|
|
401
|
+
mkdirSync(validation.target_path, { recursive: true });
|
|
402
|
+
const session = {
|
|
403
|
+
session_id: sessionId,
|
|
404
|
+
workspace_path: validation.target_path,
|
|
405
|
+
root_path: validation.root_path,
|
|
406
|
+
status: "active",
|
|
407
|
+
source: input.source,
|
|
408
|
+
objective_id: input.objective_id,
|
|
409
|
+
tracker_item_id: input.tracker_item_id,
|
|
410
|
+
created_at: now,
|
|
411
|
+
updated_at: now,
|
|
412
|
+
hooks: defaultHookSummary(),
|
|
413
|
+
};
|
|
414
|
+
const hookResult = runWorkspaceHook("after_create", session, input.hooks_timeout_ms);
|
|
415
|
+
session.hooks.after_create = workspaceHookStateFromResult(hookResult);
|
|
416
|
+
if (!hookResult.ok) {
|
|
417
|
+
session.status = "failed";
|
|
418
|
+
session.last_error = hookResult.detail;
|
|
419
|
+
}
|
|
420
|
+
registry.sessions.push(session);
|
|
421
|
+
registry.updated_at = now;
|
|
422
|
+
writeWorkspaceRegistry(registry);
|
|
423
|
+
if (!hookResult.ok) {
|
|
424
|
+
emitWorkspaceEvent("WORKSPACE_HOOK_FAILED", `Workspace hook after_create failed for ${session.session_id}`, {
|
|
425
|
+
session_id: session.session_id,
|
|
426
|
+
workspace_path: session.workspace_path,
|
|
427
|
+
detail: hookResult.detail,
|
|
428
|
+
});
|
|
429
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", `Workspace session ${session.session_id} created with failed after_create hook.`, {
|
|
430
|
+
session_id: session.session_id,
|
|
431
|
+
workspace_path: session.workspace_path,
|
|
432
|
+
detail: hookResult.detail,
|
|
433
|
+
});
|
|
434
|
+
return {
|
|
435
|
+
ok: false,
|
|
436
|
+
registry_path: registryPath,
|
|
437
|
+
session,
|
|
438
|
+
error: hookResult.detail ?? "after_create hook failed",
|
|
439
|
+
validation,
|
|
440
|
+
};
|
|
441
|
+
}
|
|
442
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_CREATED", `Workspace session ${session.session_id} created.`, {
|
|
443
|
+
session_id: session.session_id,
|
|
444
|
+
workspace_path: session.workspace_path,
|
|
445
|
+
root_path: session.root_path,
|
|
446
|
+
source: session.source,
|
|
447
|
+
});
|
|
448
|
+
return { ok: true, registry_path: registryPath, session, validation };
|
|
449
|
+
}
|
|
450
|
+
export function removeWorkspaceSession(input) {
|
|
451
|
+
const registryPath = getWorkspaceSessionRegistryPath();
|
|
452
|
+
const now = new Date().toISOString();
|
|
453
|
+
let registry;
|
|
454
|
+
try {
|
|
455
|
+
registry = readWorkspaceRegistry();
|
|
456
|
+
}
|
|
457
|
+
catch (error) {
|
|
458
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
459
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", "Workspace session removal failed because the registry could not be read.", { error: message });
|
|
460
|
+
return { ok: false, registry_path: registryPath, error: message };
|
|
461
|
+
}
|
|
462
|
+
const sessionIndex = findSessionIndex(registry, input);
|
|
463
|
+
if (sessionIndex === -1) {
|
|
464
|
+
const error = "Removal only applies to managed workspace sessions already present in the registry.";
|
|
465
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", error, {
|
|
466
|
+
session_id: input.session_id,
|
|
467
|
+
workspace_path: input.workspace_path,
|
|
468
|
+
});
|
|
469
|
+
return { ok: false, registry_path: registryPath, error };
|
|
470
|
+
}
|
|
471
|
+
const session = registry.sessions[sessionIndex];
|
|
472
|
+
const expectedRootPath = resolveRuntimeWorkspaceRoot(input.root ?? session.root_path);
|
|
473
|
+
const allowExternalRoot = storeExistsSync(resolveWorkspaceRoot()) &&
|
|
474
|
+
!isInside(canonicalizeCandidatePath(resolveWorkspaceRoot()), canonicalizeCandidatePath(expectedRootPath));
|
|
475
|
+
const validation = validateManagedWorkspacePath(expectedRootPath, session.workspace_path, { allowExternalRoot });
|
|
476
|
+
if (!validation.ok) {
|
|
477
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", `Workspace session removal rejected for ${session.session_id}`, { session_id: session.session_id, validation });
|
|
478
|
+
return {
|
|
479
|
+
ok: false,
|
|
480
|
+
registry_path: registryPath,
|
|
481
|
+
session,
|
|
482
|
+
error: validation.reason,
|
|
483
|
+
validation,
|
|
484
|
+
};
|
|
485
|
+
}
|
|
486
|
+
const hookResult = runWorkspaceHook("before_remove", session, input.hooks_timeout_ms);
|
|
487
|
+
session.hooks.before_remove = workspaceHookStateFromResult(hookResult);
|
|
488
|
+
session.updated_at = now;
|
|
489
|
+
if (!hookResult.ok) {
|
|
490
|
+
session.status = "failed";
|
|
491
|
+
session.last_error = hookResult.detail;
|
|
492
|
+
registry.updated_at = now;
|
|
493
|
+
writeWorkspaceRegistry(registry);
|
|
494
|
+
emitWorkspaceEvent("WORKSPACE_HOOK_FAILED", `Workspace hook before_remove failed for ${session.session_id}`, {
|
|
495
|
+
session_id: session.session_id,
|
|
496
|
+
workspace_path: session.workspace_path,
|
|
497
|
+
detail: hookResult.detail,
|
|
498
|
+
});
|
|
499
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", `Workspace session ${session.session_id} removal aborted by hook failure.`, {
|
|
500
|
+
session_id: session.session_id,
|
|
501
|
+
workspace_path: session.workspace_path,
|
|
502
|
+
detail: hookResult.detail,
|
|
503
|
+
});
|
|
504
|
+
return {
|
|
505
|
+
ok: false,
|
|
506
|
+
registry_path: registryPath,
|
|
507
|
+
session,
|
|
508
|
+
error: hookResult.detail ?? "before_remove hook failed",
|
|
509
|
+
validation,
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
const retention = readRuntimeProfile().workspace.retention;
|
|
513
|
+
const captured = captureWorkspaceToStore(session);
|
|
514
|
+
if (retention === "archive" && !captured) {
|
|
515
|
+
const archivePath = resolve(validation.root_path, ".archive", session.session_id);
|
|
516
|
+
const archiveValidation = validateManagedWorkspacePath(validation.root_path, archivePath, { allowExternalRoot });
|
|
517
|
+
if (!archiveValidation.ok) {
|
|
518
|
+
session.status = "failed";
|
|
519
|
+
session.last_error = archiveValidation.reason;
|
|
520
|
+
registry.updated_at = now;
|
|
521
|
+
writeWorkspaceRegistry(registry);
|
|
522
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", `Archive target rejected for workspace session ${session.session_id}`, { session_id: session.session_id, validation: archiveValidation });
|
|
523
|
+
return {
|
|
524
|
+
ok: false,
|
|
525
|
+
registry_path: registryPath,
|
|
526
|
+
session,
|
|
527
|
+
error: archiveValidation.reason,
|
|
528
|
+
validation: archiveValidation,
|
|
529
|
+
};
|
|
530
|
+
}
|
|
531
|
+
if (existsSync(validation.target_path)) {
|
|
532
|
+
mkdirSync(dirname(archiveValidation.target_path), { recursive: true });
|
|
533
|
+
renameSync(validation.target_path, archiveValidation.target_path);
|
|
534
|
+
}
|
|
535
|
+
session.workspace_path = archiveValidation.target_path;
|
|
536
|
+
session.status = "archived";
|
|
537
|
+
}
|
|
538
|
+
else {
|
|
539
|
+
if (existsSync(validation.target_path)) {
|
|
540
|
+
rmSync(validation.target_path, { recursive: true, force: false });
|
|
541
|
+
}
|
|
542
|
+
session.workspace_path = captured?.workspace_path ?? session.workspace_path;
|
|
543
|
+
session.root_path = captured?.root_path ?? session.root_path;
|
|
544
|
+
session.status = retention === "archive" ? "archived" : "removed";
|
|
545
|
+
}
|
|
546
|
+
if (allowExternalRoot && existsSync(validation.root_path)) {
|
|
547
|
+
try {
|
|
548
|
+
if (readdirSync(validation.root_path).length === 0) {
|
|
549
|
+
rmSync(validation.root_path, { recursive: false, force: false });
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
catch {
|
|
553
|
+
// Best-effort cleanup for ephemeral temp roots.
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
session.updated_at = now;
|
|
557
|
+
delete session.last_error;
|
|
558
|
+
registry.updated_at = now;
|
|
559
|
+
writeWorkspaceRegistry(registry);
|
|
560
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_REMOVED", `Workspace session ${session.session_id} ${session.status}.`, {
|
|
561
|
+
session_id: session.session_id,
|
|
562
|
+
workspace_path: session.workspace_path,
|
|
563
|
+
status: session.status,
|
|
564
|
+
});
|
|
565
|
+
return { ok: true, registry_path: registryPath, session, validation };
|
|
566
|
+
}
|
|
567
|
+
export function runWorkspaceSessionHook(input) {
|
|
568
|
+
const registryPath = getWorkspaceSessionRegistryPath();
|
|
569
|
+
const now = new Date().toISOString();
|
|
570
|
+
let registry;
|
|
571
|
+
try {
|
|
572
|
+
registry = readWorkspaceRegistry();
|
|
573
|
+
}
|
|
574
|
+
catch (error) {
|
|
575
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
576
|
+
return {
|
|
577
|
+
ok: false,
|
|
578
|
+
registry_path: registryPath,
|
|
579
|
+
hook: {
|
|
580
|
+
ok: false,
|
|
581
|
+
kind: input.kind,
|
|
582
|
+
status: "failed",
|
|
583
|
+
detail: message,
|
|
584
|
+
},
|
|
585
|
+
error: message,
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
const sessionIndex = findSessionIndex(registry, input);
|
|
589
|
+
if (sessionIndex === -1) {
|
|
590
|
+
const error = "Hook execution only applies to managed workspace sessions already present in the registry.";
|
|
591
|
+
return {
|
|
592
|
+
ok: false,
|
|
593
|
+
registry_path: registryPath,
|
|
594
|
+
hook: {
|
|
595
|
+
ok: false,
|
|
596
|
+
kind: input.kind,
|
|
597
|
+
status: "failed",
|
|
598
|
+
detail: error,
|
|
599
|
+
},
|
|
600
|
+
error,
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
const session = registry.sessions[sessionIndex];
|
|
604
|
+
const hookResult = runWorkspaceHook(input.kind, session, input.hooks_timeout_ms);
|
|
605
|
+
session.hooks[input.kind] = workspaceHookStateFromResult(hookResult);
|
|
606
|
+
session.updated_at = now;
|
|
607
|
+
if (!hookResult.ok) {
|
|
608
|
+
session.status = "failed";
|
|
609
|
+
session.last_error = hookResult.detail;
|
|
610
|
+
}
|
|
611
|
+
else if (session.status === "failed" && session.last_error === hookResult.detail) {
|
|
612
|
+
delete session.last_error;
|
|
613
|
+
}
|
|
614
|
+
registry.updated_at = now;
|
|
615
|
+
writeWorkspaceRegistry(registry);
|
|
616
|
+
if (!hookResult.ok) {
|
|
617
|
+
emitWorkspaceEvent("WORKSPACE_HOOK_FAILED", `Workspace hook ${input.kind} failed for ${session.session_id}`, {
|
|
618
|
+
session_id: session.session_id,
|
|
619
|
+
workspace_path: session.workspace_path,
|
|
620
|
+
detail: hookResult.detail,
|
|
621
|
+
});
|
|
622
|
+
emitWorkspaceEvent("WORKSPACE_SESSION_FAILED", `Workspace session ${session.session_id} recorded failed hook ${input.kind}.`, {
|
|
623
|
+
session_id: session.session_id,
|
|
624
|
+
workspace_path: session.workspace_path,
|
|
625
|
+
detail: hookResult.detail,
|
|
626
|
+
});
|
|
627
|
+
}
|
|
628
|
+
return {
|
|
629
|
+
ok: hookResult.ok,
|
|
630
|
+
registry_path: registryPath,
|
|
631
|
+
session,
|
|
632
|
+
hook: hookResult,
|
|
633
|
+
error: hookResult.ok ? undefined : hookResult.detail,
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
//# sourceMappingURL=workspace-manager.js.map
|