oh-my-copilot 4.13.21 → 4.13.45
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +8 -0
- package/agents/code-reviewer.agent.md +24 -6
- package/agents/designer.agent.md +12 -0
- package/bridge/cli.cjs +1978 -1214
- package/bridge/mcp-server.cjs +678 -261
- package/bridge/runtime-cli.cjs +55 -8
- package/bridge/team.js +507 -38
- package/dist/__tests__/agent-registry.test.js +92 -1
- package/dist/__tests__/agent-registry.test.js.map +1 -1
- package/dist/__tests__/bedrock-model-routing.test.js +18 -17
- package/dist/__tests__/bedrock-model-routing.test.js.map +1 -1
- package/dist/__tests__/delegation-enforcer.test.js +46 -6
- package/dist/__tests__/delegation-enforcer.test.js.map +1 -1
- package/dist/__tests__/hooks/session-end-cleanup.test.d.ts +2 -0
- package/dist/__tests__/hooks/session-end-cleanup.test.d.ts.map +1 -0
- package/dist/__tests__/hooks/session-end-cleanup.test.js +93 -0
- package/dist/__tests__/hooks/session-end-cleanup.test.js.map +1 -0
- package/dist/__tests__/hud/stdin.test.d.ts +2 -0
- package/dist/__tests__/hud/stdin.test.d.ts.map +1 -0
- package/dist/__tests__/hud/stdin.test.js +556 -0
- package/dist/__tests__/hud/stdin.test.js.map +1 -0
- package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts +2 -0
- package/dist/__tests__/model-routing-override-three-site-sync.test.d.ts.map +1 -0
- package/dist/__tests__/model-routing-override-three-site-sync.test.js +75 -0
- package/dist/__tests__/model-routing-override-three-site-sync.test.js.map +1 -0
- package/dist/__tests__/non-claude-provider-detection.test.js +37 -0
- package/dist/__tests__/non-claude-provider-detection.test.js.map +1 -1
- package/dist/__tests__/package-dir-resolution-regression.test.js +25 -0
- package/dist/__tests__/package-dir-resolution-regression.test.js.map +1 -1
- package/dist/__tests__/pre-tool-enforcer.test.js +51 -0
- package/dist/__tests__/pre-tool-enforcer.test.js.map +1 -1
- package/dist/__tests__/session-start-script-context.test.js +121 -0
- package/dist/__tests__/session-start-script-context.test.js.map +1 -1
- package/dist/__tests__/setup-claude-md-script.test.js +44 -1
- package/dist/__tests__/setup-claude-md-script.test.js.map +1 -1
- package/dist/agents/definitions.d.ts.map +1 -1
- package/dist/agents/definitions.js +2 -1
- package/dist/agents/definitions.js.map +1 -1
- package/dist/autoresearch/__tests__/decide-outcome.test.d.ts +2 -0
- package/dist/autoresearch/__tests__/decide-outcome.test.d.ts.map +1 -0
- package/dist/autoresearch/__tests__/decide-outcome.test.js +69 -0
- package/dist/autoresearch/__tests__/decide-outcome.test.js.map +1 -0
- package/dist/autoresearch/runtime.d.ts.map +1 -1
- package/dist/autoresearch/runtime.js +16 -2
- package/dist/autoresearch/runtime.js.map +1 -1
- package/dist/cli/__tests__/launch.test.js +23 -0
- package/dist/cli/__tests__/launch.test.js.map +1 -1
- package/dist/cli/__tests__/self-improve-paths.test.js +3 -2
- package/dist/cli/__tests__/self-improve-paths.test.js.map +1 -1
- package/dist/cli/__tests__/team.test.js +100 -0
- package/dist/cli/__tests__/team.test.js.map +1 -1
- package/dist/cli/commands/__tests__/team-role-shorthand.test.js +30 -0
- package/dist/cli/commands/__tests__/team-role-shorthand.test.js.map +1 -1
- package/dist/cli/commands/__tests__/team.test.js +48 -1
- package/dist/cli/commands/__tests__/team.test.js.map +1 -1
- package/dist/cli/commands/team.d.ts +2 -0
- package/dist/cli/commands/team.d.ts.map +1 -1
- package/dist/cli/commands/team.js +9 -2
- package/dist/cli/commands/team.js.map +1 -1
- package/dist/cli/launch.d.ts.map +1 -1
- package/dist/cli/launch.js +7 -0
- package/dist/cli/launch.js.map +1 -1
- package/dist/cli/team.d.ts.map +1 -1
- package/dist/cli/team.js +34 -3
- package/dist/cli/team.js.map +1 -1
- package/dist/config/__tests__/loader.test.js +35 -4
- package/dist/config/__tests__/loader.test.js.map +1 -1
- package/dist/config/__tests__/models.test.js +126 -3
- package/dist/config/__tests__/models.test.js.map +1 -1
- package/dist/config/models.d.ts +8 -0
- package/dist/config/models.d.ts.map +1 -1
- package/dist/config/models.js +119 -8
- package/dist/config/models.js.map +1 -1
- package/dist/features/delegation-enforcer.d.ts.map +1 -1
- package/dist/features/delegation-enforcer.js +10 -9
- package/dist/features/delegation-enforcer.js.map +1 -1
- package/dist/hooks/__tests__/bridge-routing.test.js +1 -0
- package/dist/hooks/__tests__/bridge-routing.test.js.map +1 -1
- package/dist/hooks/autopilot/enforcement.d.ts.map +1 -1
- package/dist/hooks/autopilot/enforcement.js +19 -0
- package/dist/hooks/autopilot/enforcement.js.map +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +26 -4
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/persistent-mode/stop-hook-blocking.test.js +37 -1
- package/dist/hooks/persistent-mode/stop-hook-blocking.test.js.map +1 -1
- package/dist/hooks/session-end/index.d.ts +9 -2
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +90 -35
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hud/stdin.d.ts +12 -1
- package/dist/hud/stdin.d.ts.map +1 -1
- package/dist/hud/stdin.js +123 -18
- package/dist/hud/stdin.js.map +1 -1
- package/dist/hud/types.d.ts +1 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js.map +1 -1
- package/dist/installer/__tests__/session-start-template.test.d.ts +2 -0
- package/dist/installer/__tests__/session-start-template.test.d.ts.map +1 -0
- package/dist/installer/__tests__/session-start-template.test.js +174 -0
- package/dist/installer/__tests__/session-start-template.test.js.map +1 -0
- package/dist/lib/__tests__/mode-state-io.test.js +75 -12
- package/dist/lib/__tests__/mode-state-io.test.js.map +1 -1
- package/dist/lib/mode-state-io.d.ts +11 -0
- package/dist/lib/mode-state-io.d.ts.map +1 -1
- package/dist/lib/mode-state-io.js +64 -0
- package/dist/lib/mode-state-io.js.map +1 -1
- package/dist/lib/worktree-paths.d.ts +12 -0
- package/dist/lib/worktree-paths.d.ts.map +1 -1
- package/dist/lib/worktree-paths.js +75 -0
- package/dist/lib/worktree-paths.js.map +1 -1
- package/dist/planning/__tests__/artifacts.test.d.ts +2 -0
- package/dist/planning/__tests__/artifacts.test.d.ts.map +1 -0
- package/dist/planning/__tests__/artifacts.test.js +486 -0
- package/dist/planning/__tests__/artifacts.test.js.map +1 -0
- package/dist/planning/artifact-names.d.ts +13 -0
- package/dist/planning/artifact-names.d.ts.map +1 -0
- package/dist/planning/artifact-names.js +108 -0
- package/dist/planning/artifact-names.js.map +1 -0
- package/dist/planning/artifacts.d.ts +47 -0
- package/dist/planning/artifacts.d.ts.map +1 -0
- package/dist/planning/artifacts.js +221 -0
- package/dist/planning/artifacts.js.map +1 -0
- package/dist/team/__tests__/api-interop.compatibility.test.js +194 -5
- package/dist/team/__tests__/api-interop.compatibility.test.js.map +1 -1
- package/dist/team/__tests__/model-contract.test.js +92 -2
- package/dist/team/__tests__/model-contract.test.js.map +1 -1
- package/dist/team/__tests__/runtime-v2.dispatch.test.js +30 -0
- package/dist/team/__tests__/runtime-v2.dispatch.test.js.map +1 -1
- package/dist/team/__tests__/scaling.test.js +39 -1
- package/dist/team/__tests__/scaling.test.js.map +1 -1
- package/dist/team/__tests__/worker-bootstrap.test.js +6 -0
- package/dist/team/__tests__/worker-bootstrap.test.js.map +1 -1
- package/dist/team/api-interop.d.ts.map +1 -1
- package/dist/team/api-interop.js +89 -2
- package/dist/team/api-interop.js.map +1 -1
- package/dist/team/delegation-evidence.d.ts +5 -0
- package/dist/team/delegation-evidence.d.ts.map +1 -0
- package/dist/team/delegation-evidence.js +29 -0
- package/dist/team/delegation-evidence.js.map +1 -0
- package/dist/team/model-contract.d.ts +8 -0
- package/dist/team/model-contract.d.ts.map +1 -1
- package/dist/team/model-contract.js +13 -0
- package/dist/team/model-contract.js.map +1 -1
- package/dist/team/runtime-v2.d.ts +2 -1
- package/dist/team/runtime-v2.d.ts.map +1 -1
- package/dist/team/runtime-v2.js +3 -1
- package/dist/team/runtime-v2.js.map +1 -1
- package/dist/team/state/tasks.d.ts +4 -1
- package/dist/team/state/tasks.d.ts.map +1 -1
- package/dist/team/state/tasks.js +38 -1
- package/dist/team/state/tasks.js.map +1 -1
- package/dist/team/team-ops.d.ts +4 -1
- package/dist/team/team-ops.d.ts.map +1 -1
- package/dist/team/team-ops.js +2 -2
- package/dist/team/team-ops.js.map +1 -1
- package/dist/team/types.d.ts +22 -1
- package/dist/team/types.d.ts.map +1 -1
- package/dist/team/types.js.map +1 -1
- package/dist/team/worker-bootstrap.d.ts.map +1 -1
- package/dist/team/worker-bootstrap.js +6 -1
- package/dist/team/worker-bootstrap.js.map +1 -1
- package/dist/tools/__tests__/state-tools.test.js +322 -5
- package/dist/tools/__tests__/state-tools.test.js.map +1 -1
- package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts +2 -0
- package/dist/tools/__tests__/wiki-tools-working-directory.test.d.ts.map +1 -0
- package/dist/tools/__tests__/wiki-tools-working-directory.test.js +66 -0
- package/dist/tools/__tests__/wiki-tools-working-directory.test.js.map +1 -0
- package/dist/tools/state-tools.d.ts +3 -10
- package/dist/tools/state-tools.d.ts.map +1 -1
- package/dist/tools/state-tools.js +393 -91
- package/dist/tools/state-tools.js.map +1 -1
- package/dist/tools/wiki-tools.js +8 -8
- package/dist/tools/wiki-tools.js.map +1 -1
- package/docs/GETTING-STARTED.md +7 -0
- package/docs/copilot-instructions.md +1 -1
- package/package.json +1 -1
- package/scripts/lib/model-routing-override-message.mjs +21 -0
- package/scripts/persistent-mode.cjs +36 -0
- package/scripts/persistent-mode.mjs +36 -0
- package/scripts/post-tool-verifier.mjs +1 -0
- package/scripts/pre-tool-enforcer.mjs +60 -0
- package/scripts/session-start.mjs +63 -0
- package/scripts/setup-copilot-instructions.sh +12 -2
- package/skills/configure-notifications/SKILL.md +3 -3
- package/skills/mcp-setup/SKILL.md +60 -45
- package/skills/omc-setup/SKILL.md +3 -3
- package/skills/omc-setup/phases/01-install-claude-md.md +1 -1
- package/skills/omc-setup/phases/04-welcome.md +8 -8
- package/skills/project-session-manager/SKILL.md +2 -2
- package/skills/project-session-manager/psm.sh +1 -1
- package/templates/hooks/lib/model-routing-override-message.mjs +21 -0
- package/templates/hooks/session-start.mjs +62 -0
package/bridge/team.js
CHANGED
|
@@ -2105,15 +2105,42 @@ var init_ssrf_guard = __esm({
|
|
|
2105
2105
|
});
|
|
2106
2106
|
|
|
2107
2107
|
// src/config/models.ts
|
|
2108
|
+
function readEnvValue(key) {
|
|
2109
|
+
const value = process.env[key]?.trim();
|
|
2110
|
+
return value || void 0;
|
|
2111
|
+
}
|
|
2108
2112
|
function resolveTierModelFromEnv(tier) {
|
|
2109
2113
|
for (const key of TIER_ENV_KEYS[tier]) {
|
|
2110
|
-
const value =
|
|
2114
|
+
const value = readEnvValue(key);
|
|
2115
|
+
if (value) {
|
|
2116
|
+
return value;
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
return void 0;
|
|
2120
|
+
}
|
|
2121
|
+
function getDirectModelEnvValue() {
|
|
2122
|
+
for (const key of DIRECT_MODEL_ENV_KEYS) {
|
|
2123
|
+
const value = readEnvValue(key);
|
|
2111
2124
|
if (value) {
|
|
2112
2125
|
return value;
|
|
2113
2126
|
}
|
|
2114
2127
|
}
|
|
2115
2128
|
return void 0;
|
|
2116
2129
|
}
|
|
2130
|
+
function getProviderDetectionModelEnvValues() {
|
|
2131
|
+
const directModel = getDirectModelEnvValue();
|
|
2132
|
+
if (directModel) {
|
|
2133
|
+
return [directModel];
|
|
2134
|
+
}
|
|
2135
|
+
const values = /* @__PURE__ */ new Set();
|
|
2136
|
+
for (const tier of INHERIT_TIER_PRIORITY) {
|
|
2137
|
+
const value = resolveTierModelFromEnv(tier);
|
|
2138
|
+
if (value) {
|
|
2139
|
+
values.add(value);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
return [...values];
|
|
2143
|
+
}
|
|
2117
2144
|
function getDefaultModelHigh() {
|
|
2118
2145
|
return resolveTierModelFromEnv("HIGH") || BUILTIN_TIER_MODEL_DEFAULTS.HIGH;
|
|
2119
2146
|
}
|
|
@@ -2146,17 +2173,19 @@ function isBedrock() {
|
|
|
2146
2173
|
if (modelId && /^((us|eu|ap|global)\.anthropic\.|anthropic\.claude)/i.test(modelId)) {
|
|
2147
2174
|
return true;
|
|
2148
2175
|
}
|
|
2176
|
+
if (modelId && /^arn:aws(-[^:]+)?:bedrock:/i.test(modelId) && /:(inference-profile|application-inference-profile)\//i.test(modelId) && modelId.toLowerCase().includes("claude")) {
|
|
2177
|
+
return true;
|
|
2178
|
+
}
|
|
2149
2179
|
return false;
|
|
2150
2180
|
}
|
|
2151
2181
|
function isVertexAI() {
|
|
2152
2182
|
if (process.env.CLAUDE_CODE_USE_VERTEX === "1") {
|
|
2153
2183
|
return true;
|
|
2154
2184
|
}
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
return false;
|
|
2185
|
+
return hasVertexModelId(getProviderDetectionModelEnvValues());
|
|
2186
|
+
}
|
|
2187
|
+
function hasVertexModelId(modelIds) {
|
|
2188
|
+
return modelIds.some((modelId) => modelId.toLowerCase().startsWith("vertex_ai/"));
|
|
2160
2189
|
}
|
|
2161
2190
|
function isProviderSpecificModelId(modelId) {
|
|
2162
2191
|
return isBedrockModelId(modelId) || isVertexModelId(modelId);
|
|
@@ -2195,11 +2224,13 @@ function isNonCopilotProvider() {
|
|
|
2195
2224
|
}
|
|
2196
2225
|
return false;
|
|
2197
2226
|
}
|
|
2198
|
-
var TIER_ENV_KEYS, COPILOT_FAMILY_DEFAULTS, BUILTIN_TIER_MODEL_DEFAULTS, COPILOT_FAMILY_HIGH_VARIANTS, BUILTIN_EXTERNAL_MODEL_DEFAULTS;
|
|
2227
|
+
var DIRECT_MODEL_ENV_KEYS, INHERIT_TIER_PRIORITY, TIER_ENV_KEYS, COPILOT_FAMILY_DEFAULTS, BUILTIN_TIER_MODEL_DEFAULTS, COPILOT_FAMILY_HIGH_VARIANTS, BUILTIN_EXTERNAL_MODEL_DEFAULTS;
|
|
2199
2228
|
var init_models = __esm({
|
|
2200
2229
|
"src/config/models.ts"() {
|
|
2201
2230
|
"use strict";
|
|
2202
2231
|
init_ssrf_guard();
|
|
2232
|
+
DIRECT_MODEL_ENV_KEYS = ["CLAUDE_MODEL", "ANTHROPIC_MODEL"];
|
|
2233
|
+
INHERIT_TIER_PRIORITY = ["MEDIUM", "HIGH", "LOW"];
|
|
2203
2234
|
TIER_ENV_KEYS = {
|
|
2204
2235
|
LOW: [
|
|
2205
2236
|
"OMC_MODEL_LOW",
|
|
@@ -3192,6 +3223,7 @@ var init_definitions = __esm({
|
|
|
3192
3223
|
init_utils();
|
|
3193
3224
|
init_loader();
|
|
3194
3225
|
init_strict_mode_guidance();
|
|
3226
|
+
init_models();
|
|
3195
3227
|
init_architect();
|
|
3196
3228
|
init_designer();
|
|
3197
3229
|
init_writer();
|
|
@@ -3275,6 +3307,9 @@ var init_definitions = __esm({
|
|
|
3275
3307
|
|
|
3276
3308
|
// src/features/delegation-enforcer.ts
|
|
3277
3309
|
function normalizeToCcAlias(model) {
|
|
3310
|
+
if (isProviderSpecificModelId(model)) {
|
|
3311
|
+
return model;
|
|
3312
|
+
}
|
|
3278
3313
|
const family = resolveClaudeFamily(model);
|
|
3279
3314
|
return family ? CC_FAMILY_TO_ALIAS[family] ?? model : model;
|
|
3280
3315
|
}
|
|
@@ -3451,6 +3486,9 @@ function resolveCliBinaryPath(binary) {
|
|
|
3451
3486
|
resolvedPathCache.set(binary, resolvedPath);
|
|
3452
3487
|
return resolvedPath;
|
|
3453
3488
|
}
|
|
3489
|
+
function shouldUseClaudeBareMode(env = process.env) {
|
|
3490
|
+
return typeof env.ANTHROPIC_API_KEY === "string" && env.ANTHROPIC_API_KEY.trim().length > 0;
|
|
3491
|
+
}
|
|
3454
3492
|
function getContract(agentType) {
|
|
3455
3493
|
const contract = CONTRACTS[agentType];
|
|
3456
3494
|
if (!contract) {
|
|
@@ -3560,6 +3598,9 @@ var init_model_contract = __esm({
|
|
|
3560
3598
|
installInstructions: "Install Claude CLI: https://claude.ai/download",
|
|
3561
3599
|
buildLaunchArgs(model, extraFlags = []) {
|
|
3562
3600
|
const args = ["--dangerously-skip-permissions"];
|
|
3601
|
+
if (shouldUseClaudeBareMode() && !extraFlags.includes("--bare")) {
|
|
3602
|
+
args.push("--bare");
|
|
3603
|
+
}
|
|
3563
3604
|
if (model) {
|
|
3564
3605
|
const resolved = isProviderSpecificModelId(model) ? model : normalizeToCcAlias(model);
|
|
3565
3606
|
args.push("--model", resolved);
|
|
@@ -3749,7 +3790,7 @@ function generateWorkerOverlay(params) {
|
|
|
3749
3790
|
const shutdownAckPath = buildTeamStateInstructionPath(teamName, instructionStateRoot, "workers", workerName, "shutdown-ack.json");
|
|
3750
3791
|
const claimTaskCommand = formatOmcCliInvocation(`team api claim-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"worker\\":\\"${workerName}\\"}" --json`);
|
|
3751
3792
|
const sendAckCommand = formatOmcCliInvocation(`team api send-message --input "{\\"team_name\\":\\"${teamName}\\",\\"from_worker\\":\\"${workerName}\\",\\"to_worker\\":\\"leader-fixed\\",\\"body\\":\\"ACK: ${workerName} initialized\\"}" --json`);
|
|
3752
|
-
const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
|
|
3793
|
+
const completeTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"completed\\",\\"claim_token\\":\\"<claim_token>\\",\\"result\\":\\"Summary: <what changed>\\\\nVerification: <tests/checks run>\\\\nSubagent skip reason: worker protocol forbids nested subagents; completed focused probe in-session\\"}" --json`);
|
|
3753
3794
|
const failTaskCommand = formatOmcCliInvocation(`team api transition-task-status --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"from\\":\\"in_progress\\",\\"to\\":\\"failed\\",\\"claim_token\\":\\"<claim_token>\\"}" --json`);
|
|
3754
3795
|
const readTaskCommand = formatOmcCliInvocation(`team api read-task --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\"}" --json`);
|
|
3755
3796
|
const releaseClaimCommand = formatOmcCliInvocation(`team api release-task-claim --input "{\\"team_name\\":\\"${teamName}\\",\\"task_id\\":\\"<id>\\",\\"claim_token\\":\\"<claim_token>\\",\\"worker\\":\\"${workerName}\\"}" --json`);
|
|
@@ -3802,6 +3843,11 @@ Use the CLI API for all task lifecycle operations. Do NOT directly edit task fil
|
|
|
3802
3843
|
- Complete task: \`${completeTaskCommand}\`
|
|
3803
3844
|
- Fail task: \`${failTaskCommand}\`
|
|
3804
3845
|
- Release claim (rollback): \`${releaseClaimCommand}\`
|
|
3846
|
+
- Delegation compliance evidence (required for broad delegated tasks):
|
|
3847
|
+
- The completion command MUST include a \`result\` string with summary and verification evidence.
|
|
3848
|
+
- Because worker protocol forbids nested sub-agents, use: \`Subagent skip reason: <why in-session execution was safer/sufficient>\`
|
|
3849
|
+
- Only if the leader explicitly grants an exception to spawn nested help, use: \`Subagent spawn evidence: <count, child task names/thread ids, and integrated findings>\`
|
|
3850
|
+
- Completion is rejected with \`missing_delegation_compliance_evidence\` when required evidence is absent.
|
|
3805
3851
|
|
|
3806
3852
|
## Canonical Team State Root
|
|
3807
3853
|
- Resolve the team state root in this order: \`OMC_TEAM_STATE_ROOT\` env -> worker identity \`team_state_root\` -> config/manifest \`team_state_root\` -> ${params.cwd}/.omcp/state/team/${teamName}.
|
|
@@ -5934,8 +5980,8 @@ async function startMergeOrchestrator(config) {
|
|
|
5934
5980
|
let persisted = { lastShas: {} };
|
|
5935
5981
|
if (existsSync18(persistedPath)) {
|
|
5936
5982
|
try {
|
|
5937
|
-
const { readFileSync:
|
|
5938
|
-
persisted = JSON.parse(
|
|
5983
|
+
const { readFileSync: readFileSync13 } = await import("node:fs");
|
|
5984
|
+
persisted = JSON.parse(readFileSync13(persistedPath, "utf-8"));
|
|
5939
5985
|
} catch {
|
|
5940
5986
|
persisted = { lastShas: {} };
|
|
5941
5987
|
}
|
|
@@ -6324,8 +6370,8 @@ async function recoverFromRestart(config) {
|
|
|
6324
6370
|
let persistedShasLoaded = 0;
|
|
6325
6371
|
if (existsSync18(persistedPath)) {
|
|
6326
6372
|
try {
|
|
6327
|
-
const { readFileSync:
|
|
6328
|
-
const persisted = JSON.parse(
|
|
6373
|
+
const { readFileSync: readFileSync13 } = await import("node:fs");
|
|
6374
|
+
const persisted = JSON.parse(readFileSync13(persistedPath, "utf-8"));
|
|
6329
6375
|
persistedShasLoaded = Object.keys(persisted.lastShas ?? {}).length;
|
|
6330
6376
|
} catch {
|
|
6331
6377
|
persistedShasLoaded = 0;
|
|
@@ -6538,7 +6584,7 @@ function buildV2TaskInstruction(teamName, workerName, task, taskId, cliOutputCon
|
|
|
6538
6584
|
{}
|
|
6539
6585
|
);
|
|
6540
6586
|
const completeTaskCommand = formatOmcCliInvocation(
|
|
6541
|
-
`team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>" })}' --json`
|
|
6587
|
+
`team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "completed", claim_token: "<claim_token>", result: "Summary: <what changed>\\nVerification: <tests/checks run>\\nSubagent skip reason: worker protocol forbids nested subagents; completed focused probe in-session" })}' --json`
|
|
6542
6588
|
);
|
|
6543
6589
|
const failTaskCommand = formatOmcCliInvocation(
|
|
6544
6590
|
`team api transition-task-status --input '${JSON.stringify({ team_name: teamName, task_id: taskId, from: "in_progress", to: "failed", claim_token: "<claim_token>" })}' --json`
|
|
@@ -6553,6 +6599,7 @@ function buildV2TaskInstruction(teamName, workerName, task, taskId, cliOutputCon
|
|
|
6553
6599
|
`2. Do the work described below.`,
|
|
6554
6600
|
`3. On completion (use claim_token from step 1):`,
|
|
6555
6601
|
` ${completeTaskCommand}`,
|
|
6602
|
+
` The result field is required for completion evidence. For broad delegated tasks, include either "Subagent skip reason: <why no nested worker was needed/allowed>" or, only when explicitly allowed by the leader, "Subagent spawn evidence: <child task names/thread ids and integrated findings>".`,
|
|
6556
6603
|
`4. On failure (use claim_token from step 1):`,
|
|
6557
6604
|
` ${failTaskCommand}`,
|
|
6558
6605
|
`5. ACK/progress replies are not a stop signal. Keep executing your assigned or next feasible work until the task is actually complete or failed, then transition and exit.`,
|
|
@@ -6916,6 +6963,7 @@ async function startTeamV2(config) {
|
|
|
6916
6963
|
status: "pending",
|
|
6917
6964
|
owner: null,
|
|
6918
6965
|
result: null,
|
|
6966
|
+
...config.tasks[i].delegation ? { delegation: config.tasks[i].delegation } : {},
|
|
6919
6967
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
6920
6968
|
}, null, 2), "utf-8");
|
|
6921
6969
|
}
|
|
@@ -7292,10 +7340,10 @@ async function requeueDeadWorkerTasks(teamName, deadWorkerNames, cwd) {
|
|
|
7292
7340
|
await writeFile8(sidecarPath, JSON.stringify(sidecar, null, 2), "utf-8");
|
|
7293
7341
|
const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, task.id));
|
|
7294
7342
|
try {
|
|
7295
|
-
const { readFileSync:
|
|
7343
|
+
const { readFileSync: readFileSync13, writeFileSync: writeFileSync4 } = await import("fs");
|
|
7296
7344
|
const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
|
|
7297
7345
|
withFileLockSync2(taskPath2 + ".lock", () => {
|
|
7298
|
-
const raw =
|
|
7346
|
+
const raw = readFileSync13(taskPath2, "utf-8");
|
|
7299
7347
|
const taskData = JSON.parse(raw);
|
|
7300
7348
|
if (taskData.status === "in_progress") {
|
|
7301
7349
|
taskData.status = "pending";
|
|
@@ -7325,7 +7373,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
|
|
|
7325
7373
|
"team.runtime-v2.processCliWorkerVerdicts appendTeamEvent failed"
|
|
7326
7374
|
);
|
|
7327
7375
|
const { rename: rename3 } = await import("fs/promises");
|
|
7328
|
-
const { readFileSync:
|
|
7376
|
+
const { readFileSync: readFileSync13, writeFileSync: writeFileSync4, existsSync: fsExistsSync } = await import("fs");
|
|
7329
7377
|
const { withFileLockSync: withFileLockSync2 } = await Promise.resolve().then(() => (init_file_lock(), file_lock_exports));
|
|
7330
7378
|
for (const worker of config.workers) {
|
|
7331
7379
|
const outputFile = worker.output_file;
|
|
@@ -7359,7 +7407,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
|
|
|
7359
7407
|
const taskPath2 = absPath(cwd, TeamPaths.taskFile(sanitized, taskId));
|
|
7360
7408
|
if (!fsExistsSync(taskPath2)) continue;
|
|
7361
7409
|
try {
|
|
7362
|
-
const taskRaw =
|
|
7410
|
+
const taskRaw = readFileSync13(taskPath2, "utf-8");
|
|
7363
7411
|
const taskData = JSON.parse(taskRaw);
|
|
7364
7412
|
if (taskData.owner === worker.name && taskData.status === "in_progress") {
|
|
7365
7413
|
targetTaskId = taskId;
|
|
@@ -7387,7 +7435,7 @@ async function processCliWorkerVerdicts(teamName, cwd) {
|
|
|
7387
7435
|
let transitionOk = false;
|
|
7388
7436
|
try {
|
|
7389
7437
|
withFileLockSync2(targetTaskPath + ".lock", () => {
|
|
7390
|
-
const raw =
|
|
7438
|
+
const raw = readFileSync13(targetTaskPath, "utf-8");
|
|
7391
7439
|
const taskData = JSON.parse(raw);
|
|
7392
7440
|
if (taskData.status !== "in_progress" || taskData.owner !== worker.name) {
|
|
7393
7441
|
return;
|
|
@@ -7936,10 +7984,10 @@ var init_runtime_v2 = __esm({
|
|
|
7936
7984
|
// src/cli/team.ts
|
|
7937
7985
|
import { randomUUID as randomUUID6 } from "crypto";
|
|
7938
7986
|
import { spawn } from "child_process";
|
|
7939
|
-
import { existsSync as
|
|
7987
|
+
import { existsSync as existsSync22, mkdirSync as mkdirSync3, readFileSync as readFileSync12, writeFileSync as writeFileSync3 } from "fs";
|
|
7940
7988
|
import { readFile as readFile11, readdir as readdir3, rm as rm5 } from "fs/promises";
|
|
7941
7989
|
import { homedir as homedir3 } from "os";
|
|
7942
|
-
import { dirname as dirname16, join as
|
|
7990
|
+
import { dirname as dirname16, join as join25 } from "path";
|
|
7943
7991
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
7944
7992
|
|
|
7945
7993
|
// src/team/api-interop.ts
|
|
@@ -8010,7 +8058,31 @@ async function claimTask(taskId, workerName, expectedVersion, deps) {
|
|
|
8010
8058
|
if (!lock.ok) return { ok: false, error: "claim_conflict" };
|
|
8011
8059
|
return lock.value;
|
|
8012
8060
|
}
|
|
8013
|
-
|
|
8061
|
+
function extractDelegationComplianceEvidence(task, terminalData) {
|
|
8062
|
+
const plan = task.delegation;
|
|
8063
|
+
if (!plan || plan.mode === "none") return null;
|
|
8064
|
+
if (plan.mode === "optional" && plan.required_parallel_probe !== true) return null;
|
|
8065
|
+
const result = typeof terminalData?.result === "string" ? terminalData.result : "";
|
|
8066
|
+
const spawnMatch = result.match(/^\s*Subagent spawn evidence:\s*(.+)$/im);
|
|
8067
|
+
if (spawnMatch?.[1]?.trim()) {
|
|
8068
|
+
const detail = spawnMatch[1].trim();
|
|
8069
|
+
if (!/^none\b|^0\b/i.test(detail)) {
|
|
8070
|
+
return { status: "spawned", source: "terminal_result", detail, recorded_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
8071
|
+
}
|
|
8072
|
+
}
|
|
8073
|
+
if (plan.skip_allowed_reason_required === true) {
|
|
8074
|
+
const skipMatch = result.match(/^\s*Subagent skip reason:\s*(.+)$/im);
|
|
8075
|
+
if (skipMatch?.[1]?.trim()) {
|
|
8076
|
+
return { status: "skipped", source: "terminal_result", detail: skipMatch[1].trim(), recorded_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
8077
|
+
}
|
|
8078
|
+
}
|
|
8079
|
+
return null;
|
|
8080
|
+
}
|
|
8081
|
+
function requiresDelegationComplianceEvidence(task) {
|
|
8082
|
+
const plan = task.delegation;
|
|
8083
|
+
return !!plan && (plan.mode === "auto" || plan.mode === "required" || plan.required_parallel_probe === true);
|
|
8084
|
+
}
|
|
8085
|
+
async function transitionTaskStatus(taskId, from, to, claimToken, terminalData, deps) {
|
|
8014
8086
|
if (!deps.canTransitionTaskStatus(from, to)) return { ok: false, error: "invalid_transition" };
|
|
8015
8087
|
const lock = await deps.withTaskClaimLock(deps.teamName, taskId, deps.cwd, async () => {
|
|
8016
8088
|
const current = await deps.readTask(deps.teamName, taskId, deps.cwd);
|
|
@@ -8023,10 +8095,19 @@ async function transitionTaskStatus(taskId, from, to, claimToken, deps) {
|
|
|
8023
8095
|
return { ok: false, error: "claim_conflict" };
|
|
8024
8096
|
}
|
|
8025
8097
|
if (new Date(v.claim.leased_until) <= /* @__PURE__ */ new Date()) return { ok: false, error: "lease_expired" };
|
|
8098
|
+
const normalizedResult = typeof terminalData?.result === "string" ? terminalData.result : void 0;
|
|
8099
|
+
const normalizedError = typeof terminalData?.error === "string" ? terminalData.error : void 0;
|
|
8100
|
+
const delegationCompliance = to === "completed" ? extractDelegationComplianceEvidence(v, terminalData) : null;
|
|
8101
|
+
if (to === "completed" && requiresDelegationComplianceEvidence(v) && !delegationCompliance) {
|
|
8102
|
+
return { ok: false, error: "missing_delegation_compliance_evidence" };
|
|
8103
|
+
}
|
|
8026
8104
|
const updated = {
|
|
8027
8105
|
...v,
|
|
8028
8106
|
status: to,
|
|
8029
8107
|
completed_at: to === "completed" ? (/* @__PURE__ */ new Date()).toISOString() : v.completed_at,
|
|
8108
|
+
result: to === "completed" ? normalizedResult : void 0,
|
|
8109
|
+
error: to === "failed" ? normalizedError : void 0,
|
|
8110
|
+
delegation_compliance: to === "completed" ? delegationCompliance ?? v.delegation_compliance : v.delegation_compliance,
|
|
8030
8111
|
claim: void 0,
|
|
8031
8112
|
version: v.version + 1
|
|
8032
8113
|
};
|
|
@@ -8386,8 +8467,8 @@ async function teamClaimTask(teamName, taskId, workerName, expectedVersion, cwd)
|
|
|
8386
8467
|
writeAtomic
|
|
8387
8468
|
});
|
|
8388
8469
|
}
|
|
8389
|
-
async function teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd) {
|
|
8390
|
-
return transitionTaskStatus(taskId, from, to, claimToken, {
|
|
8470
|
+
async function teamTransitionTaskStatus(teamName, taskId, from, to, claimToken, cwd, terminalData) {
|
|
8471
|
+
return transitionTaskStatus(taskId, from, to, claimToken, terminalData, {
|
|
8391
8472
|
teamName,
|
|
8392
8473
|
cwd,
|
|
8393
8474
|
readTask: teamReadTask,
|
|
@@ -8880,7 +8961,7 @@ async function resumeTeam(teamName, cwd) {
|
|
|
8880
8961
|
init_runtime_v2();
|
|
8881
8962
|
init_git_worktree();
|
|
8882
8963
|
init_swallowed_error();
|
|
8883
|
-
var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change"]);
|
|
8964
|
+
var TEAM_UPDATE_TASK_MUTABLE_FIELDS = /* @__PURE__ */ new Set(["subject", "description", "blocked_by", "requires_code_change", "delegation"]);
|
|
8884
8965
|
var TEAM_UPDATE_TASK_REQUEST_FIELDS = /* @__PURE__ */ new Set(["team_name", "task_id", "workingDirectory", ...TEAM_UPDATE_TASK_MUTABLE_FIELDS]);
|
|
8885
8966
|
var TEAM_API_OPERATIONS = [
|
|
8886
8967
|
"send-message",
|
|
@@ -8933,6 +9014,60 @@ function parseValidatedTaskIdArray(value, fieldName) {
|
|
|
8933
9014
|
}
|
|
8934
9015
|
return taskIds;
|
|
8935
9016
|
}
|
|
9017
|
+
function parseTaskDelegationPlan(value) {
|
|
9018
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
9019
|
+
throw new Error("delegation must be an object");
|
|
9020
|
+
}
|
|
9021
|
+
const raw = value;
|
|
9022
|
+
const mode = raw.mode;
|
|
9023
|
+
if (mode !== "none" && mode !== "optional" && mode !== "auto" && mode !== "required") {
|
|
9024
|
+
throw new Error("delegation.mode must be one of: none, optional, auto, required");
|
|
9025
|
+
}
|
|
9026
|
+
const plan = { mode };
|
|
9027
|
+
if ("max_parallel_subtasks" in raw) {
|
|
9028
|
+
if (!isFiniteInteger(raw.max_parallel_subtasks) || raw.max_parallel_subtasks < 1) {
|
|
9029
|
+
throw new Error("delegation.max_parallel_subtasks must be a positive integer when provided");
|
|
9030
|
+
}
|
|
9031
|
+
plan.max_parallel_subtasks = raw.max_parallel_subtasks;
|
|
9032
|
+
}
|
|
9033
|
+
if ("required_parallel_probe" in raw) {
|
|
9034
|
+
if (typeof raw.required_parallel_probe !== "boolean") throw new Error("delegation.required_parallel_probe must be a boolean when provided");
|
|
9035
|
+
plan.required_parallel_probe = raw.required_parallel_probe;
|
|
9036
|
+
}
|
|
9037
|
+
if ("spawn_before_serial_search_threshold" in raw) {
|
|
9038
|
+
if (!isFiniteInteger(raw.spawn_before_serial_search_threshold) || raw.spawn_before_serial_search_threshold < 1) {
|
|
9039
|
+
throw new Error("delegation.spawn_before_serial_search_threshold must be a positive integer when provided");
|
|
9040
|
+
}
|
|
9041
|
+
plan.spawn_before_serial_search_threshold = raw.spawn_before_serial_search_threshold;
|
|
9042
|
+
}
|
|
9043
|
+
if ("child_model_policy" in raw) {
|
|
9044
|
+
const policy = raw.child_model_policy;
|
|
9045
|
+
if (policy !== "standard" && policy !== "fast" && policy !== "inherit" && policy !== "frontier") {
|
|
9046
|
+
throw new Error("delegation.child_model_policy must be one of: standard, fast, inherit, frontier");
|
|
9047
|
+
}
|
|
9048
|
+
plan.child_model_policy = policy;
|
|
9049
|
+
}
|
|
9050
|
+
if ("child_model" in raw) {
|
|
9051
|
+
if (typeof raw.child_model !== "string") throw new Error("delegation.child_model must be a string when provided");
|
|
9052
|
+
plan.child_model = raw.child_model;
|
|
9053
|
+
}
|
|
9054
|
+
if ("subtask_candidates" in raw) {
|
|
9055
|
+
if (!Array.isArray(raw.subtask_candidates) || !raw.subtask_candidates.every((item) => typeof item === "string")) {
|
|
9056
|
+
throw new Error("delegation.subtask_candidates must be an array of strings when provided");
|
|
9057
|
+
}
|
|
9058
|
+
plan.subtask_candidates = raw.subtask_candidates;
|
|
9059
|
+
}
|
|
9060
|
+
if ("child_report_format" in raw) {
|
|
9061
|
+
const format = raw.child_report_format;
|
|
9062
|
+
if (format !== "bullets" && format !== "json") throw new Error("delegation.child_report_format must be bullets or json when provided");
|
|
9063
|
+
plan.child_report_format = format;
|
|
9064
|
+
}
|
|
9065
|
+
if ("skip_allowed_reason_required" in raw) {
|
|
9066
|
+
if (typeof raw.skip_allowed_reason_required !== "boolean") throw new Error("delegation.skip_allowed_reason_required must be a boolean when provided");
|
|
9067
|
+
plan.skip_allowed_reason_required = raw.skip_allowed_reason_required;
|
|
9068
|
+
}
|
|
9069
|
+
return plan;
|
|
9070
|
+
}
|
|
8936
9071
|
function teamStateExists(teamName, candidateCwd) {
|
|
8937
9072
|
if (!TEAM_NAME_SAFE_PATTERN.test(teamName)) return false;
|
|
8938
9073
|
const teamRoot = join23(candidateCwd, ".omcp", "state", "team", teamName);
|
|
@@ -9304,13 +9439,22 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
|
|
|
9304
9439
|
const owner = args.owner;
|
|
9305
9440
|
const blockedBy = args.blocked_by;
|
|
9306
9441
|
const requiresCodeChange = args.requires_code_change;
|
|
9442
|
+
let delegation;
|
|
9443
|
+
if ("delegation" in args) {
|
|
9444
|
+
try {
|
|
9445
|
+
delegation = parseTaskDelegationPlan(args.delegation);
|
|
9446
|
+
} catch (error) {
|
|
9447
|
+
return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
|
|
9448
|
+
}
|
|
9449
|
+
}
|
|
9307
9450
|
const task = await teamCreateTask(teamName, {
|
|
9308
9451
|
subject,
|
|
9309
9452
|
description,
|
|
9310
9453
|
status: "pending",
|
|
9311
9454
|
owner: owner || void 0,
|
|
9312
9455
|
blocked_by: blockedBy,
|
|
9313
|
-
requires_code_change: requiresCodeChange
|
|
9456
|
+
requires_code_change: requiresCodeChange,
|
|
9457
|
+
...delegation ? { delegation } : {}
|
|
9314
9458
|
}, cwd);
|
|
9315
9459
|
return { ok: true, operation, data: { task } };
|
|
9316
9460
|
}
|
|
@@ -9372,6 +9516,13 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
|
|
|
9372
9516
|
return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
|
|
9373
9517
|
}
|
|
9374
9518
|
}
|
|
9519
|
+
if ("delegation" in args) {
|
|
9520
|
+
try {
|
|
9521
|
+
updates.delegation = parseTaskDelegationPlan(args.delegation);
|
|
9522
|
+
} catch (error) {
|
|
9523
|
+
return { ok: false, operation, error: { code: "invalid_input", message: error.message } };
|
|
9524
|
+
}
|
|
9525
|
+
}
|
|
9375
9526
|
const task = await teamUpdateTask(teamName, taskId, updates, cwd);
|
|
9376
9527
|
return task ? { ok: true, operation, data: { task } } : { ok: false, operation, error: { code: "task_not_found", message: "task_not_found" } };
|
|
9377
9528
|
}
|
|
@@ -9395,6 +9546,8 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
|
|
|
9395
9546
|
const from = String(args.from || "").trim();
|
|
9396
9547
|
const to = String(args.to || "").trim();
|
|
9397
9548
|
const claimToken = String(args.claim_token || "").trim();
|
|
9549
|
+
const transitionResult = args.result;
|
|
9550
|
+
const transitionError = args.error;
|
|
9398
9551
|
if (!teamName || !taskId || !from || !to || !claimToken) {
|
|
9399
9552
|
return { ok: false, operation, error: { code: "invalid_input", message: "team_name, task_id, from, to, claim_token are required" } };
|
|
9400
9553
|
}
|
|
@@ -9402,7 +9555,24 @@ async function executeTeamApiOperation(operation, args, fallbackCwd) {
|
|
|
9402
9555
|
if (!allowed.has(from) || !allowed.has(to)) {
|
|
9403
9556
|
return { ok: false, operation, error: { code: "invalid_input", message: "from and to must be valid task statuses" } };
|
|
9404
9557
|
}
|
|
9405
|
-
|
|
9558
|
+
if (transitionResult !== void 0 && typeof transitionResult !== "string") {
|
|
9559
|
+
return { ok: false, operation, error: { code: "invalid_input", message: "result must be a string when provided" } };
|
|
9560
|
+
}
|
|
9561
|
+
if (transitionError !== void 0 && typeof transitionError !== "string") {
|
|
9562
|
+
return { ok: false, operation, error: { code: "invalid_input", message: "error must be a string when provided" } };
|
|
9563
|
+
}
|
|
9564
|
+
const result = await teamTransitionTaskStatus(
|
|
9565
|
+
teamName,
|
|
9566
|
+
taskId,
|
|
9567
|
+
from,
|
|
9568
|
+
to,
|
|
9569
|
+
claimToken,
|
|
9570
|
+
cwd,
|
|
9571
|
+
{
|
|
9572
|
+
result: typeof transitionResult === "string" ? transitionResult : void 0,
|
|
9573
|
+
error: typeof transitionError === "string" ? transitionError : void 0
|
|
9574
|
+
}
|
|
9575
|
+
);
|
|
9406
9576
|
return { ok: true, operation, data: result };
|
|
9407
9577
|
}
|
|
9408
9578
|
case "release-task-claim": {
|
|
@@ -9625,6 +9795,278 @@ init_tmux_session();
|
|
|
9625
9795
|
init_team_name();
|
|
9626
9796
|
init_monitor();
|
|
9627
9797
|
init_process_utils();
|
|
9798
|
+
|
|
9799
|
+
// src/planning/artifacts.ts
|
|
9800
|
+
import { readdirSync as readdirSync5, readFileSync as readFileSync11, existsSync as existsSync21 } from "fs";
|
|
9801
|
+
import { join as join24 } from "path";
|
|
9802
|
+
|
|
9803
|
+
// src/planning/artifact-names.ts
|
|
9804
|
+
import { basename as basename7 } from "path";
|
|
9805
|
+
var PLANNING_ARTIFACT_TIMESTAMP_PATTERN = /^\d{8}T\d{6}Z$/;
|
|
9806
|
+
function legacyTestSpecSlug(fileNameOrPath) {
|
|
9807
|
+
const match = basename7(fileNameOrPath).match(/^test-?spec-(?<slug>.+)\.md$/i);
|
|
9808
|
+
return match?.groups?.slug ?? null;
|
|
9809
|
+
}
|
|
9810
|
+
function requiredTimestampedTestSpecFileName(prdArtifact) {
|
|
9811
|
+
return prdArtifact.kind === "prd" && prdArtifact.timestamp ? `test-spec-${prdArtifact.timestamp}-${prdArtifact.slug}.md` : null;
|
|
9812
|
+
}
|
|
9813
|
+
function splitTimestampPrefix(rawSlug) {
|
|
9814
|
+
const separatorIndex = rawSlug.indexOf("-");
|
|
9815
|
+
if (separatorIndex === -1) {
|
|
9816
|
+
return { slug: rawSlug };
|
|
9817
|
+
}
|
|
9818
|
+
const prefix = rawSlug.slice(0, separatorIndex);
|
|
9819
|
+
if (!PLANNING_ARTIFACT_TIMESTAMP_PATTERN.test(prefix)) {
|
|
9820
|
+
return { slug: rawSlug };
|
|
9821
|
+
}
|
|
9822
|
+
return {
|
|
9823
|
+
timestamp: prefix,
|
|
9824
|
+
slug: rawSlug.slice(separatorIndex + 1)
|
|
9825
|
+
};
|
|
9826
|
+
}
|
|
9827
|
+
function parsePlanningArtifactFileName(fileNameOrPath) {
|
|
9828
|
+
const fileName = basename7(fileNameOrPath);
|
|
9829
|
+
const autoresearchDeepInterviewMatch = fileName.match(
|
|
9830
|
+
/^deep-interview-autoresearch-(?<slug>.+)\.md$/i
|
|
9831
|
+
);
|
|
9832
|
+
if (autoresearchDeepInterviewMatch?.groups?.slug) {
|
|
9833
|
+
const parsedSlug = splitTimestampPrefix(autoresearchDeepInterviewMatch.groups.slug);
|
|
9834
|
+
if (!parsedSlug.slug) return null;
|
|
9835
|
+
return {
|
|
9836
|
+
kind: "deep-interview-autoresearch",
|
|
9837
|
+
...parsedSlug
|
|
9838
|
+
};
|
|
9839
|
+
}
|
|
9840
|
+
const deepInterviewMatch = fileName.match(/^deep-interview-(?<slug>.+)\.md$/i);
|
|
9841
|
+
if (deepInterviewMatch?.groups?.slug) {
|
|
9842
|
+
const parsedSlug = splitTimestampPrefix(deepInterviewMatch.groups.slug);
|
|
9843
|
+
if (!parsedSlug.slug) return null;
|
|
9844
|
+
return {
|
|
9845
|
+
kind: "deep-interview",
|
|
9846
|
+
...parsedSlug
|
|
9847
|
+
};
|
|
9848
|
+
}
|
|
9849
|
+
const prdMatch = fileName.match(/^prd-(?<slug>.+)\.md$/i);
|
|
9850
|
+
if (prdMatch?.groups?.slug) {
|
|
9851
|
+
const parsedSlug = splitTimestampPrefix(prdMatch.groups.slug);
|
|
9852
|
+
if (!parsedSlug.slug) return null;
|
|
9853
|
+
return {
|
|
9854
|
+
kind: "prd",
|
|
9855
|
+
...parsedSlug
|
|
9856
|
+
};
|
|
9857
|
+
}
|
|
9858
|
+
const testSpecMatch = fileName.match(/^test-?spec-(?<slug>.+)\.md$/i);
|
|
9859
|
+
if (testSpecMatch?.groups?.slug) {
|
|
9860
|
+
const parsedSlug = splitTimestampPrefix(testSpecMatch.groups.slug);
|
|
9861
|
+
if (!parsedSlug.slug) return null;
|
|
9862
|
+
return {
|
|
9863
|
+
kind: "test-spec",
|
|
9864
|
+
...parsedSlug
|
|
9865
|
+
};
|
|
9866
|
+
}
|
|
9867
|
+
return null;
|
|
9868
|
+
}
|
|
9869
|
+
function comparePlanningArtifactPaths(left, right) {
|
|
9870
|
+
const leftParsed = parsePlanningArtifactFileName(left);
|
|
9871
|
+
const rightParsed = parsePlanningArtifactFileName(right);
|
|
9872
|
+
if (leftParsed?.timestamp && rightParsed?.timestamp && leftParsed.timestamp !== rightParsed.timestamp) {
|
|
9873
|
+
return leftParsed.timestamp.localeCompare(rightParsed.timestamp);
|
|
9874
|
+
}
|
|
9875
|
+
if (leftParsed?.timestamp && !rightParsed?.timestamp) {
|
|
9876
|
+
return 1;
|
|
9877
|
+
}
|
|
9878
|
+
if (!leftParsed?.timestamp && rightParsed?.timestamp) {
|
|
9879
|
+
return -1;
|
|
9880
|
+
}
|
|
9881
|
+
return left.localeCompare(right);
|
|
9882
|
+
}
|
|
9883
|
+
function selectMatchingTestSpecsForPrd(prdPath, testSpecPaths) {
|
|
9884
|
+
if (!prdPath) {
|
|
9885
|
+
return [];
|
|
9886
|
+
}
|
|
9887
|
+
const prdArtifact = parsePlanningArtifactFileName(prdPath);
|
|
9888
|
+
if (prdArtifact?.kind !== "prd") {
|
|
9889
|
+
return [];
|
|
9890
|
+
}
|
|
9891
|
+
const requiredTimestampedFileName = requiredTimestampedTestSpecFileName(prdArtifact);
|
|
9892
|
+
return (requiredTimestampedFileName ? testSpecPaths.filter((path4) => basename7(path4) === requiredTimestampedFileName) : testSpecPaths.filter((path4) => legacyTestSpecSlug(path4) === prdArtifact.slug)).sort(comparePlanningArtifactPaths);
|
|
9893
|
+
}
|
|
9894
|
+
function selectLatestPlanningArtifactPath(paths) {
|
|
9895
|
+
return [...paths].sort(comparePlanningArtifactPaths).at(-1) ?? null;
|
|
9896
|
+
}
|
|
9897
|
+
|
|
9898
|
+
// src/planning/artifacts.ts
|
|
9899
|
+
function readFileSafe(path4) {
|
|
9900
|
+
try {
|
|
9901
|
+
return readFileSync11(path4, "utf-8");
|
|
9902
|
+
} catch {
|
|
9903
|
+
return null;
|
|
9904
|
+
}
|
|
9905
|
+
}
|
|
9906
|
+
function escapeRegex(value) {
|
|
9907
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
9908
|
+
}
|
|
9909
|
+
function getSectionContent(markdown, heading) {
|
|
9910
|
+
const headingRe = new RegExp(
|
|
9911
|
+
`^##\\s+${escapeRegex(heading)}[ \\t]*$`,
|
|
9912
|
+
"im"
|
|
9913
|
+
);
|
|
9914
|
+
const headingMatch = headingRe.exec(markdown);
|
|
9915
|
+
if (!headingMatch || headingMatch.index === void 0) return null;
|
|
9916
|
+
const bodyStart = headingMatch.index + headingMatch[0].length;
|
|
9917
|
+
const rest = markdown.slice(bodyStart).replace(/^\r?\n/, "");
|
|
9918
|
+
const nextHeadingMatch = /\r?\n##\s+/.exec(rest);
|
|
9919
|
+
const body = (nextHeadingMatch ? rest.slice(0, nextHeadingMatch.index) : rest).trim();
|
|
9920
|
+
return body.length > 0 ? body : null;
|
|
9921
|
+
}
|
|
9922
|
+
function hasRequiredSections(markdown, headings) {
|
|
9923
|
+
return headings.every(
|
|
9924
|
+
(heading) => getSectionContent(markdown, heading) !== null
|
|
9925
|
+
);
|
|
9926
|
+
}
|
|
9927
|
+
function getPlansDirCandidates(cwd) {
|
|
9928
|
+
return [join24(cwd, ".omc", "plans"), join24(cwd, ".omx", "plans")];
|
|
9929
|
+
}
|
|
9930
|
+
function sortArtifactPathsDescending(paths) {
|
|
9931
|
+
return [...paths].sort((a, b) => comparePlanningArtifactPaths(b, a));
|
|
9932
|
+
}
|
|
9933
|
+
function hasCompletePlanningPair(prdPath, matchingTestSpecPaths) {
|
|
9934
|
+
if (matchingTestSpecPaths.length === 0) {
|
|
9935
|
+
return false;
|
|
9936
|
+
}
|
|
9937
|
+
const prd = readFileSafe(prdPath);
|
|
9938
|
+
const testSpec = readFileSafe(matchingTestSpecPaths[0]);
|
|
9939
|
+
if (!prd || !testSpec) {
|
|
9940
|
+
return false;
|
|
9941
|
+
}
|
|
9942
|
+
return hasRequiredSections(prd, [
|
|
9943
|
+
"Acceptance criteria",
|
|
9944
|
+
"Requirement coverage map"
|
|
9945
|
+
]) && hasRequiredSections(testSpec, [
|
|
9946
|
+
"Unit coverage",
|
|
9947
|
+
"Verification mapping"
|
|
9948
|
+
]);
|
|
9949
|
+
}
|
|
9950
|
+
function readPlanningArtifacts(cwd) {
|
|
9951
|
+
let entries;
|
|
9952
|
+
const prdPaths = [];
|
|
9953
|
+
const testSpecPaths = [];
|
|
9954
|
+
for (const plansDir of getPlansDirCandidates(cwd)) {
|
|
9955
|
+
if (!existsSync21(plansDir)) {
|
|
9956
|
+
continue;
|
|
9957
|
+
}
|
|
9958
|
+
try {
|
|
9959
|
+
entries = readdirSync5(plansDir);
|
|
9960
|
+
} catch {
|
|
9961
|
+
continue;
|
|
9962
|
+
}
|
|
9963
|
+
for (const entry of entries) {
|
|
9964
|
+
if (entry.startsWith("prd-") && entry.endsWith(".md")) {
|
|
9965
|
+
prdPaths.push(join24(plansDir, entry));
|
|
9966
|
+
} else if (entry.startsWith("test-spec-") && entry.endsWith(".md")) {
|
|
9967
|
+
testSpecPaths.push(join24(plansDir, entry));
|
|
9968
|
+
}
|
|
9969
|
+
}
|
|
9970
|
+
}
|
|
9971
|
+
return {
|
|
9972
|
+
prdPaths: sortArtifactPathsDescending(prdPaths),
|
|
9973
|
+
testSpecPaths: sortArtifactPathsDescending(testSpecPaths)
|
|
9974
|
+
};
|
|
9975
|
+
}
|
|
9976
|
+
function decodeQuotedValue(raw) {
|
|
9977
|
+
const normalized = raw.trim();
|
|
9978
|
+
if (!normalized) return null;
|
|
9979
|
+
try {
|
|
9980
|
+
return JSON.parse(normalized);
|
|
9981
|
+
} catch {
|
|
9982
|
+
if (normalized.startsWith('"') && normalized.endsWith('"') || normalized.startsWith("'") && normalized.endsWith("'")) {
|
|
9983
|
+
return normalized.slice(1, -1);
|
|
9984
|
+
}
|
|
9985
|
+
return null;
|
|
9986
|
+
}
|
|
9987
|
+
}
|
|
9988
|
+
function launchHintPattern(mode) {
|
|
9989
|
+
return mode === "team" ? /(?<command>(?:om[cx]\s+team|\$team)(?:\s+ralph)?(?:\s+(?<count>\d+)(?::(?<role>[a-z][a-z0-9-]*))?)?\s+(?<task>"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?<flags>(?:\s+--[\w-]+)*))/gi : /(?<command>(?:om[cx]\s+ralph|\$ralph)\s+(?<task>"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*')(?<flags>(?:\s+--[\w-]+)*))/gi;
|
|
9990
|
+
}
|
|
9991
|
+
function collectLaunchHintMatches(content, mode) {
|
|
9992
|
+
return [...content.matchAll(launchHintPattern(mode))];
|
|
9993
|
+
}
|
|
9994
|
+
function selectLaunchHintMatch(matches, normalizedTask, normalizedCommand) {
|
|
9995
|
+
const decodedMatches = matches.flatMap((match) => {
|
|
9996
|
+
const command = match[0]?.trim();
|
|
9997
|
+
const task = match.groups?.task ? decodeQuotedValue(match.groups.task) : null;
|
|
9998
|
+
if (!command || task == null) return [];
|
|
9999
|
+
const flags = match.groups?.flags ?? "";
|
|
10000
|
+
const workerCount = match.groups?.count ? Number.parseInt(match.groups.count, 10) : void 0;
|
|
10001
|
+
return [{
|
|
10002
|
+
command,
|
|
10003
|
+
task,
|
|
10004
|
+
...workerCount == null ? {} : { workerCount },
|
|
10005
|
+
agentType: match.groups?.role || void 0,
|
|
10006
|
+
linkedRalph: /\sralph(?:\s|$)/.test(command) || parseFlags(flags).linkedRalph
|
|
10007
|
+
}];
|
|
10008
|
+
});
|
|
10009
|
+
const matchesToConsider = normalizedCommand ? decodedMatches.filter((match) => match.command === normalizedCommand) : normalizedTask ? decodedMatches.filter((match) => match.task.trim() === normalizedTask) : decodedMatches;
|
|
10010
|
+
if (matchesToConsider.length === 0) return { status: "no-match" };
|
|
10011
|
+
if (matchesToConsider.length > 1) return { status: "ambiguous" };
|
|
10012
|
+
return { status: "unique", ...matchesToConsider[0] };
|
|
10013
|
+
}
|
|
10014
|
+
function parseFlags(flagStr) {
|
|
10015
|
+
return {
|
|
10016
|
+
linkedRalph: /--linked-ralph/.test(flagStr)
|
|
10017
|
+
};
|
|
10018
|
+
}
|
|
10019
|
+
function readApprovedExecutionLaunchHintOutcome(cwd, mode, options = {}) {
|
|
10020
|
+
const artifacts = readPlanningArtifacts(cwd);
|
|
10021
|
+
if (artifacts.prdPaths.length === 0) return { status: "absent" };
|
|
10022
|
+
const prdPath = options.prdPath ? artifacts.prdPaths.includes(options.prdPath) ? options.prdPath : null : selectLatestPlanningArtifactPath(artifacts.prdPaths);
|
|
10023
|
+
const matchingTestSpecs = selectMatchingTestSpecsForPrd(
|
|
10024
|
+
prdPath,
|
|
10025
|
+
artifacts.testSpecPaths
|
|
10026
|
+
);
|
|
10027
|
+
if (!prdPath) return { status: "absent" };
|
|
10028
|
+
if (artifacts.testSpecPaths.length > 0 && matchingTestSpecs.length === 0) {
|
|
10029
|
+
return { status: "absent" };
|
|
10030
|
+
}
|
|
10031
|
+
const content = readFileSafe(prdPath);
|
|
10032
|
+
if (!content) return { status: "absent" };
|
|
10033
|
+
const selected = selectLaunchHintMatch(
|
|
10034
|
+
collectLaunchHintMatches(content, mode),
|
|
10035
|
+
options.task?.trim(),
|
|
10036
|
+
options.command?.trim()
|
|
10037
|
+
);
|
|
10038
|
+
if (selected.status === "ambiguous") return { status: "ambiguous" };
|
|
10039
|
+
if (selected.status !== "unique") return { status: "absent" };
|
|
10040
|
+
if (options.requirePlanningComplete && !hasCompletePlanningPair(prdPath, matchingTestSpecs)) {
|
|
10041
|
+
return { status: "incomplete" };
|
|
10042
|
+
}
|
|
10043
|
+
if (mode === "team") {
|
|
10044
|
+
return {
|
|
10045
|
+
status: "resolved",
|
|
10046
|
+
hint: {
|
|
10047
|
+
mode: "team",
|
|
10048
|
+
command: selected.command,
|
|
10049
|
+
task: selected.task,
|
|
10050
|
+
workerCount: selected.workerCount,
|
|
10051
|
+
agentType: selected.agentType,
|
|
10052
|
+
linkedRalph: selected.linkedRalph,
|
|
10053
|
+
sourcePath: prdPath
|
|
10054
|
+
}
|
|
10055
|
+
};
|
|
10056
|
+
}
|
|
10057
|
+
return {
|
|
10058
|
+
status: "resolved",
|
|
10059
|
+
hint: {
|
|
10060
|
+
mode: "ralph",
|
|
10061
|
+
command: selected.command,
|
|
10062
|
+
task: selected.task,
|
|
10063
|
+
linkedRalph: selected.linkedRalph,
|
|
10064
|
+
sourcePath: prdPath
|
|
10065
|
+
}
|
|
10066
|
+
};
|
|
10067
|
+
}
|
|
10068
|
+
|
|
10069
|
+
// src/cli/team.ts
|
|
9628
10070
|
var JOB_ID_PATTERN = /^omc-[a-z0-9]{1,16}$/;
|
|
9629
10071
|
var VALID_CLI_AGENT_TYPES = /* @__PURE__ */ new Set(["claude", "copilot", "codex", "gemini", "cursor"]);
|
|
9630
10072
|
var SUBCOMMANDS = /* @__PURE__ */ new Set(["start", "status", "wait", "cleanup", "resume", "shutdown", "api", "help", "--help", "-h"]);
|
|
@@ -9658,31 +10100,31 @@ function assertTeamSpawnAllowed(env = process.env) {
|
|
|
9658
10100
|
);
|
|
9659
10101
|
}
|
|
9660
10102
|
function resolveJobsDir(env = process.env) {
|
|
9661
|
-
return env.OMC_JOBS_DIR ||
|
|
10103
|
+
return env.OMC_JOBS_DIR || join25(homedir3(), ".omcp", "team-jobs");
|
|
9662
10104
|
}
|
|
9663
10105
|
function resolveRuntimeCliPath(env = process.env) {
|
|
9664
10106
|
if (env.OMC_RUNTIME_CLI_PATH) {
|
|
9665
10107
|
return env.OMC_RUNTIME_CLI_PATH;
|
|
9666
10108
|
}
|
|
9667
10109
|
const moduleDir = dirname16(fileURLToPath3(import.meta.url));
|
|
9668
|
-
return
|
|
10110
|
+
return join25(moduleDir, "../../bridge/runtime-cli.cjs");
|
|
9669
10111
|
}
|
|
9670
10112
|
function ensureJobsDir(jobsDir) {
|
|
9671
|
-
if (!
|
|
10113
|
+
if (!existsSync22(jobsDir)) {
|
|
9672
10114
|
mkdirSync3(jobsDir, { recursive: true });
|
|
9673
10115
|
}
|
|
9674
10116
|
}
|
|
9675
10117
|
function jobPath(jobsDir, jobId) {
|
|
9676
|
-
return
|
|
10118
|
+
return join25(jobsDir, `${jobId}.json`);
|
|
9677
10119
|
}
|
|
9678
10120
|
function resultArtifactPath(jobsDir, jobId) {
|
|
9679
|
-
return
|
|
10121
|
+
return join25(jobsDir, `${jobId}-result.json`);
|
|
9680
10122
|
}
|
|
9681
10123
|
function panesArtifactPath(jobsDir, jobId) {
|
|
9682
|
-
return
|
|
10124
|
+
return join25(jobsDir, `${jobId}-panes.json`);
|
|
9683
10125
|
}
|
|
9684
10126
|
function teamStateRoot2(cwd, teamName) {
|
|
9685
|
-
return
|
|
10127
|
+
return join25(cwd, ".omcp", "state", "team", teamName);
|
|
9686
10128
|
}
|
|
9687
10129
|
function validateJobId(jobId) {
|
|
9688
10130
|
if (!JOB_ID_PATTERN.test(jobId)) {
|
|
@@ -9722,7 +10164,7 @@ async function resolveCleanupPaneEvidence(job, jobsDir, jobId) {
|
|
|
9722
10164
|
}
|
|
9723
10165
|
function readJobFromDisk(jobId, jobsDir) {
|
|
9724
10166
|
try {
|
|
9725
|
-
const content =
|
|
10167
|
+
const content = readFileSync12(jobPath(jobsDir, jobId), "utf-8");
|
|
9726
10168
|
return parseJsonSafe(content);
|
|
9727
10169
|
} catch {
|
|
9728
10170
|
return null;
|
|
@@ -9751,7 +10193,7 @@ function generateJobId(now = Date.now()) {
|
|
|
9751
10193
|
}
|
|
9752
10194
|
function convergeWithResultArtifact(jobId, job, jobsDir) {
|
|
9753
10195
|
try {
|
|
9754
|
-
const artifactRaw =
|
|
10196
|
+
const artifactRaw = readFileSync12(resultArtifactPath(jobsDir, jobId), "utf-8");
|
|
9755
10197
|
const artifactParsed = parseJsonSafe(artifactRaw);
|
|
9756
10198
|
if (artifactParsed?.status === "completed" || artifactParsed?.status === "failed") {
|
|
9757
10199
|
return {
|
|
@@ -10495,9 +10937,9 @@ function parseLegacyStartAlias(args) {
|
|
|
10495
10937
|
if (!spec) return null;
|
|
10496
10938
|
const match = spec.match(/^(\d+):([a-zA-Z0-9_-]+)(?::([a-zA-Z0-9_-]+))?$/);
|
|
10497
10939
|
if (!match) return null;
|
|
10498
|
-
|
|
10940
|
+
let workerCount = toInt(match[1], "worker-count");
|
|
10499
10941
|
if (workerCount < 1) throw new Error("worker-count must be >= 1");
|
|
10500
|
-
|
|
10942
|
+
let agentType = normalizeAgentType(match[2]);
|
|
10501
10943
|
const role = match[3] || void 0;
|
|
10502
10944
|
index += 1;
|
|
10503
10945
|
let json = false;
|
|
@@ -10532,8 +10974,35 @@ function parseLegacyStartAlias(args) {
|
|
|
10532
10974
|
}
|
|
10533
10975
|
taskParts.push(token);
|
|
10534
10976
|
}
|
|
10535
|
-
|
|
10977
|
+
let task = taskParts.join(" ").trim();
|
|
10536
10978
|
if (!task) throw new Error("Legacy start alias requires a task string");
|
|
10979
|
+
const shortFollowup = ["team", "/team", "team please", "run team", "start team"].includes(task.toLowerCase());
|
|
10980
|
+
if (shortFollowup) {
|
|
10981
|
+
const approvedHintOutcome = readApprovedExecutionLaunchHintOutcome(cwd, "team", {
|
|
10982
|
+
requirePlanningComplete: true
|
|
10983
|
+
});
|
|
10984
|
+
if (approvedHintOutcome.status === "ambiguous") {
|
|
10985
|
+
throw new Error("approved_execution_hint_ambiguous:team");
|
|
10986
|
+
}
|
|
10987
|
+
if (approvedHintOutcome.status === "incomplete") {
|
|
10988
|
+
throw new Error("approved_execution_hint_incomplete:team");
|
|
10989
|
+
}
|
|
10990
|
+
if (approvedHintOutcome.status === "resolved") {
|
|
10991
|
+
task = approvedHintOutcome.hint.task;
|
|
10992
|
+
workerCount = approvedHintOutcome.hint.workerCount ?? workerCount;
|
|
10993
|
+
agentType = approvedHintOutcome.hint.agentType ? normalizeAgentType(approvedHintOutcome.hint.agentType) : agentType;
|
|
10994
|
+
ralph = approvedHintOutcome.hint.linkedRalph === true ? true : ralph;
|
|
10995
|
+
}
|
|
10996
|
+
} else {
|
|
10997
|
+
const command = `omc team ${ralph ? "ralph " : ""}${spec} ${JSON.stringify(task)}`;
|
|
10998
|
+
const approvedHintOutcome = readApprovedExecutionLaunchHintOutcome(cwd, "team", {
|
|
10999
|
+
task,
|
|
11000
|
+
command
|
|
11001
|
+
});
|
|
11002
|
+
if (approvedHintOutcome.status === "ambiguous") {
|
|
11003
|
+
throw new Error("approved_execution_hint_ambiguous:team");
|
|
11004
|
+
}
|
|
11005
|
+
}
|
|
10537
11006
|
return {
|
|
10538
11007
|
workerCount,
|
|
10539
11008
|
agentType,
|