@gajae-code/coding-agent 0.6.4 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/dist/types/async/job-manager.d.ts +3 -1
- package/dist/types/cli/daemon-cli.d.ts +25 -0
- package/dist/types/cli/migrate-cli.d.ts +20 -0
- package/dist/types/cli/notify-cli.d.ts +23 -0
- package/dist/types/cli/setup-cli.d.ts +20 -1
- package/dist/types/commands/daemon.d.ts +41 -0
- package/dist/types/commands/migrate.d.ts +33 -0
- package/dist/types/commands/notify.d.ts +41 -0
- package/dist/types/config/keybindings.d.ts +4 -0
- package/dist/types/config/model-profile-activation.d.ts +12 -0
- package/dist/types/config/model-profiles.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +3 -3
- package/dist/types/config/models-config-schema.d.ts +5 -0
- package/dist/types/config/settings-schema.d.ts +38 -0
- package/dist/types/coordinator/contract.d.ts +1 -1
- package/dist/types/daemon/builtin.d.ts +20 -0
- package/dist/types/daemon/control-types.d.ts +57 -0
- package/dist/types/daemon/runtime.d.ts +25 -0
- package/dist/types/extensibility/extensions/types.d.ts +8 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -2
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/session-layout.d.ts +59 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +47 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +1 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +5 -4
- package/dist/types/gjc-runtime/state-schema.d.ts +2 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +38 -7
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +15 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +21 -4
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +1 -1
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +1 -1
- package/dist/types/harness-control-plane/storage.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +12 -4
- package/dist/types/migrate/action-planner.d.ts +11 -0
- package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
- package/dist/types/migrate/adapters/codex.d.ts +5 -0
- package/dist/types/migrate/adapters/index.d.ts +45 -0
- package/dist/types/migrate/adapters/opencode.d.ts +2 -0
- package/dist/types/migrate/executor.d.ts +2 -0
- package/dist/types/migrate/mcp-mapper.d.ts +20 -0
- package/dist/types/migrate/report.d.ts +18 -0
- package/dist/types/migrate/skill-normalizer.d.ts +27 -0
- package/dist/types/migrate/types.d.ts +126 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/components/oauth-selector.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -2
- package/dist/types/modes/interactive-mode.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +10 -0
- package/dist/types/modes/types.d.ts +7 -1
- package/dist/types/notifications/config-commands.d.ts +26 -0
- package/dist/types/notifications/config.d.ts +61 -0
- package/dist/types/notifications/helpers.d.ts +55 -0
- package/dist/types/notifications/html-format.d.ts +62 -0
- package/dist/types/notifications/index.d.ts +28 -0
- package/dist/types/notifications/rate-limit-pool.d.ts +93 -0
- package/dist/types/notifications/telegram-cli.d.ts +19 -0
- package/dist/types/notifications/telegram-daemon-cli.d.ts +11 -0
- package/dist/types/notifications/telegram-daemon-control.d.ts +56 -0
- package/dist/types/notifications/telegram-daemon.d.ts +276 -0
- package/dist/types/notifications/telegram-reference.d.ts +111 -0
- package/dist/types/notifications/threaded-inbound.d.ts +58 -0
- package/dist/types/notifications/threaded-render.d.ts +66 -0
- package/dist/types/notifications/topic-registry.d.ts +67 -0
- package/dist/types/research-plan/index.d.ts +1 -0
- package/dist/types/research-plan/ledger.d.ts +33 -0
- package/dist/types/rlm/artifacts.d.ts +1 -1
- package/dist/types/rlm/index.d.ts +12 -0
- package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
- package/dist/types/session/agent-session.d.ts +39 -2
- package/dist/types/session/auth-storage.d.ts +1 -1
- package/dist/types/setup/credential-auto-import.d.ts +63 -0
- package/dist/types/setup/credential-import.d.ts +3 -0
- package/dist/types/setup/host-plugin-setup.d.ts +39 -0
- package/dist/types/skill-state/active-state.d.ts +6 -11
- package/dist/types/skill-state/canonical-skills.d.ts +3 -0
- package/dist/types/skill-state/workflow-hud.d.ts +2 -0
- package/dist/types/task/spawn-gate.d.ts +1 -10
- package/dist/types/tools/ask-answer-registry.d.ts +13 -0
- package/dist/types/tools/index.d.ts +18 -0
- package/dist/types/tools/subagent.d.ts +3 -0
- package/package.json +7 -7
- package/scripts/build-binary.ts +3 -0
- package/src/async/job-manager.ts +5 -1
- package/src/cli/daemon-cli.ts +122 -0
- package/src/cli/migrate-cli.ts +106 -0
- package/src/cli/notify-cli.ts +274 -0
- package/src/cli/setup-cli.ts +173 -84
- package/src/cli.ts +3 -0
- package/src/commands/daemon.ts +47 -0
- package/src/commands/deep-interview.ts +2 -2
- package/src/commands/migrate.ts +46 -0
- package/src/commands/notify.ts +61 -0
- package/src/commands/setup.ts +11 -1
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +7 -3
- package/src/config/model-profile-activation.ts +74 -5
- package/src/config/model-profiles.ts +7 -4
- package/src/config/model-registry.ts +6 -3
- package/src/config/models-config-schema.ts +1 -1
- package/src/config/settings-schema.ts +29 -0
- package/src/coordinator/contract.ts +3 -0
- package/src/coordinator-mcp/policy.ts +10 -2
- package/src/coordinator-mcp/server.ts +270 -1
- package/src/daemon/builtin.ts +46 -0
- package/src/daemon/control-types.ts +65 -0
- package/src/daemon/runtime.ts +51 -0
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +0 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +28 -24
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +51 -47
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +33 -13
- package/src/extensibility/custom-commands/loader.ts +0 -7
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +8 -0
- package/src/extensibility/gjc-plugins/injection.ts +23 -4
- package/src/extensibility/gjc-plugins/state.ts +16 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +51 -18
- package/src/gjc-runtime/deep-interview-runtime.ts +49 -23
- package/src/gjc-runtime/goal-mode-request.ts +26 -11
- package/src/gjc-runtime/launch-tmux.ts +6 -1
- package/src/gjc-runtime/ralplan-runtime.ts +79 -50
- package/src/gjc-runtime/session-layout.ts +180 -0
- package/src/gjc-runtime/session-resolution.ts +217 -0
- package/src/gjc-runtime/state-graph.ts +1 -2
- package/src/gjc-runtime/state-migrations.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +247 -124
- package/src/gjc-runtime/state-schema.ts +2 -0
- package/src/gjc-runtime/state-writer.ts +289 -41
- package/src/gjc-runtime/team-runtime.ts +43 -19
- package/src/gjc-runtime/tmux-sessions.ts +7 -1
- package/src/gjc-runtime/ultragoal-guard.ts +102 -4
- package/src/gjc-runtime/ultragoal-runtime.ts +226 -60
- package/src/gjc-runtime/workflow-command-ref.ts +1 -2
- package/src/gjc-runtime/workflow-manifest.generated.json +27 -2
- package/src/gjc-runtime/workflow-manifest.ts +12 -3
- package/src/goals/tools/goal-tool.ts +11 -2
- package/src/harness-control-plane/storage.ts +14 -4
- package/src/hooks/native-skill-hook.ts +38 -12
- package/src/hooks/skill-state.ts +178 -83
- package/src/internal-urls/docs-index.generated.ts +9 -6
- package/src/main.ts +30 -0
- package/src/migrate/action-planner.ts +318 -0
- package/src/migrate/adapters/claude-code.ts +39 -0
- package/src/migrate/adapters/codex.ts +70 -0
- package/src/migrate/adapters/index.ts +277 -0
- package/src/migrate/adapters/opencode.ts +52 -0
- package/src/migrate/executor.ts +81 -0
- package/src/migrate/mcp-mapper.ts +152 -0
- package/src/migrate/report.ts +104 -0
- package/src/migrate/skill-normalizer.ts +80 -0
- package/src/migrate/types.ts +163 -0
- package/src/modes/acp/acp-event-mapper.ts +1 -0
- package/src/modes/bridge/bridge-mode.ts +2 -2
- package/src/modes/components/custom-editor.ts +30 -20
- package/src/modes/components/hook-editor.ts +7 -2
- package/src/modes/components/oauth-selector.ts +19 -0
- package/src/modes/controllers/event-controller.ts +20 -0
- package/src/modes/controllers/selector-controller.ts +80 -17
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/runtime-init.ts +1 -0
- package/src/modes/shared/agent-wire/event-contract.ts +1 -0
- package/src/modes/shared/agent-wire/event-envelope.ts +1 -0
- package/src/modes/shared/agent-wire/event-observation.ts +16 -0
- package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
- package/src/modes/shared/agent-wire/unattended-session.ts +22 -0
- package/src/modes/types.ts +7 -1
- package/src/modes/utils/ui-helpers.ts +23 -0
- package/src/notifications/config-commands.ts +50 -0
- package/src/notifications/config.ts +107 -0
- package/src/notifications/helpers.ts +135 -0
- package/src/notifications/html-format.ts +389 -0
- package/src/notifications/index.ts +663 -0
- package/src/notifications/rate-limit-pool.ts +179 -0
- package/src/notifications/telegram-cli.ts +194 -0
- package/src/notifications/telegram-daemon-cli.ts +74 -0
- package/src/notifications/telegram-daemon-control.ts +370 -0
- package/src/notifications/telegram-daemon.ts +1370 -0
- package/src/notifications/telegram-reference.ts +335 -0
- package/src/notifications/threaded-inbound.ts +80 -0
- package/src/notifications/threaded-render.ts +155 -0
- package/src/notifications/topic-registry.ts +133 -0
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -2
- package/src/research-plan/index.ts +1 -0
- package/src/research-plan/ledger.ts +177 -0
- package/src/rlm/artifacts.ts +12 -3
- package/src/rlm/index.ts +26 -0
- package/src/runtime-mcp/config-writer.ts +46 -0
- package/src/sdk.ts +16 -0
- package/src/session/agent-session.ts +128 -24
- package/src/session/auth-storage.ts +3 -0
- package/src/session/session-dump-format.ts +43 -2
- package/src/session/session-manager.ts +39 -5
- package/src/setup/credential-auto-import.ts +258 -0
- package/src/setup/credential-import.ts +17 -0
- package/src/setup/hermes/templates/operator-instructions.v1.md +10 -0
- package/src/setup/hermes-setup.ts +1 -1
- package/src/setup/host-plugin-setup.ts +142 -0
- package/src/skill-state/active-state.ts +72 -108
- package/src/skill-state/canonical-skills.ts +4 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +28 -109
- package/src/skill-state/workflow-hud.ts +4 -2
- package/src/skill-state/workflow-state-contract.ts +3 -3
- package/src/slash-commands/builtin-registry.ts +4 -1
- package/src/task/agents.ts +1 -22
- package/src/task/executor.ts +5 -1
- package/src/task/index.ts +1 -41
- package/src/task/spawn-gate.ts +1 -38
- package/src/task/types.ts +1 -1
- package/src/tools/ask-answer-registry.ts +25 -0
- package/src/tools/ask.ts +108 -16
- package/src/tools/computer.ts +58 -4
- package/src/tools/image-gen.ts +5 -8
- package/src/tools/index.ts +19 -0
- package/src/tools/inspect-image.ts +16 -11
- package/src/tools/subagent-render.ts +7 -0
- package/src/tools/subagent.ts +38 -7
- package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
- package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
- package/src/prompts/agents/explore.md +0 -58
- package/src/prompts/agents/plan.md +0 -49
- package/src/prompts/agents/reviewer.md +0 -141
- package/src/prompts/agents/task.md +0 -16
- package/src/prompts/review-request.md +0 -70
|
@@ -184,7 +184,8 @@ type CoordinatorEventKind =
|
|
|
184
184
|
| "question.answered"
|
|
185
185
|
| "report.written"
|
|
186
186
|
| "tmux.delivery_succeeded"
|
|
187
|
-
| "tmux.delivery_failed"
|
|
187
|
+
| "tmux.delivery_failed"
|
|
188
|
+
| "delegation.started";
|
|
188
189
|
|
|
189
190
|
interface CoordinatorEvent {
|
|
190
191
|
schema_version: 1;
|
|
@@ -411,9 +412,116 @@ function toolSchema(name: CoordinatorToolName): {
|
|
|
411
412
|
},
|
|
412
413
|
};
|
|
413
414
|
}
|
|
415
|
+
const delegateWorkflow = workflowForDelegateTool(name);
|
|
416
|
+
if (delegateWorkflow) {
|
|
417
|
+
return {
|
|
418
|
+
name,
|
|
419
|
+
description: delegateToolDescription(delegateWorkflow),
|
|
420
|
+
inputSchema: {
|
|
421
|
+
type: "object",
|
|
422
|
+
properties: {
|
|
423
|
+
cwd,
|
|
424
|
+
task: {
|
|
425
|
+
type: "string",
|
|
426
|
+
description: "Delegated task or objective to run through the selected GJC workflow.",
|
|
427
|
+
},
|
|
428
|
+
prompt: { type: "string", description: "Alias for task; accepted when task is absent." },
|
|
429
|
+
allow_mutation: allowMutation,
|
|
430
|
+
session_id: {
|
|
431
|
+
type: "string",
|
|
432
|
+
description:
|
|
433
|
+
"Optional existing GJC coordinator bridge session id to reuse; omitted starts a fresh session.",
|
|
434
|
+
},
|
|
435
|
+
queue: {
|
|
436
|
+
type: "boolean",
|
|
437
|
+
description: "When reusing a session with an active turn, queue instead of failing.",
|
|
438
|
+
},
|
|
439
|
+
force: {
|
|
440
|
+
type: "boolean",
|
|
441
|
+
description: "When reusing a session with an active turn, supersede it before sending.",
|
|
442
|
+
},
|
|
443
|
+
model: {
|
|
444
|
+
type: "string",
|
|
445
|
+
description: "Optional model hint passed in prompt metadata; no provider default is implied.",
|
|
446
|
+
},
|
|
447
|
+
await_completion: { type: "boolean", description: "If true, poll the turn until terminal or timeout." },
|
|
448
|
+
timeout_ms: {
|
|
449
|
+
type: "number",
|
|
450
|
+
description: "Bounded await timeout; same cap semantics as gjc_coordinator_await_turn.",
|
|
451
|
+
},
|
|
452
|
+
poll_interval_ms: { type: "number", description: "Bounded await polling interval." },
|
|
453
|
+
lines: { type: "number", description: "Bounded advisory tail lines returned with await/read payloads." },
|
|
454
|
+
},
|
|
455
|
+
required: ["cwd", "allow_mutation"],
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
}
|
|
414
459
|
return { name, description: "List known scoped GJC coordinator bridge sessions.", inputSchema: common };
|
|
415
460
|
}
|
|
416
461
|
|
|
462
|
+
type DelegateWorkflow = "plan" | "execute" | "team";
|
|
463
|
+
|
|
464
|
+
function workflowForDelegateTool(name: string): DelegateWorkflow | null {
|
|
465
|
+
switch (name) {
|
|
466
|
+
case "gjc_delegate_plan":
|
|
467
|
+
return "plan";
|
|
468
|
+
case "gjc_delegate_execute":
|
|
469
|
+
return "execute";
|
|
470
|
+
case "gjc_delegate_team":
|
|
471
|
+
return "team";
|
|
472
|
+
default:
|
|
473
|
+
return null;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function workflowSkill(workflow: DelegateWorkflow): "ralplan" | "ultragoal" | "team" {
|
|
478
|
+
switch (workflow) {
|
|
479
|
+
case "plan":
|
|
480
|
+
return "ralplan";
|
|
481
|
+
case "execute":
|
|
482
|
+
return "ultragoal";
|
|
483
|
+
case "team":
|
|
484
|
+
return "team";
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function delegateToolDescription(workflow: DelegateWorkflow): string {
|
|
489
|
+
switch (workflow) {
|
|
490
|
+
case "plan":
|
|
491
|
+
return "Delegate consensus planning to GJC: start a session and run /skill:ralplan to completion, returning durable turn status and artifact references.";
|
|
492
|
+
case "execute":
|
|
493
|
+
return "Delegate execution to GJC: start a session and run /skill:ultragoal to completion, returning durable turn status and artifact references.";
|
|
494
|
+
case "team":
|
|
495
|
+
return "Delegate parallel team execution to GJC: start a session and run /skill:team to completion, returning durable turn status and artifact references.";
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
function workflowPrompt(
|
|
500
|
+
workflow: DelegateWorkflow,
|
|
501
|
+
toolName: string,
|
|
502
|
+
canonicalCwd: string,
|
|
503
|
+
task: string,
|
|
504
|
+
options: { mutationRequested: boolean; model?: string | null },
|
|
505
|
+
): string {
|
|
506
|
+
const skill = workflowSkill(workflow);
|
|
507
|
+
const model = options.model && options.model.trim().length > 0 ? options.model.trim() : "none";
|
|
508
|
+
const mutationIntent = options.mutationRequested ? "mutation requested" : "read-only";
|
|
509
|
+
return [
|
|
510
|
+
`/skill:${skill}`,
|
|
511
|
+
"",
|
|
512
|
+
`Delegated by coordinator MCP tool: ${toolName}`,
|
|
513
|
+
`Workflow: ${workflow}`,
|
|
514
|
+
`CWD: ${canonicalCwd}`,
|
|
515
|
+
`Mutation intent: ${mutationIntent}; coordinator startup policy remains authoritative.`,
|
|
516
|
+
`Optional model hint: ${model}`,
|
|
517
|
+
"",
|
|
518
|
+
"Task:",
|
|
519
|
+
task,
|
|
520
|
+
"",
|
|
521
|
+
"Return durable status and artifact references through GJC runtime/coordinator state. Do not expose host-facing tmux controls.",
|
|
522
|
+
].join("\n");
|
|
523
|
+
}
|
|
524
|
+
|
|
417
525
|
function normalizeSession(session: Record<string, unknown>): Record<string, unknown> {
|
|
418
526
|
return {
|
|
419
527
|
session_id: session.sessionId ?? session.session_id ?? session.name ?? "unknown",
|
|
@@ -424,6 +532,14 @@ function normalizeSession(session: Record<string, unknown>): Record<string, unkn
|
|
|
424
532
|
};
|
|
425
533
|
}
|
|
426
534
|
|
|
535
|
+
async function canonicalizePath(value: string): Promise<string> {
|
|
536
|
+
try {
|
|
537
|
+
return await fs.realpath(value);
|
|
538
|
+
} catch {
|
|
539
|
+
return path.resolve(value);
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
|
|
427
543
|
async function ensureDir(dir: string): Promise<void> {
|
|
428
544
|
await fs.mkdir(dir, { recursive: true });
|
|
429
545
|
}
|
|
@@ -1587,6 +1703,159 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
1587
1703
|
transport: { mcp: "long_poll", push_subscriptions: false },
|
|
1588
1704
|
};
|
|
1589
1705
|
}
|
|
1706
|
+
const delegateWorkflow = workflowForDelegateTool(name);
|
|
1707
|
+
if (delegateWorkflow) {
|
|
1708
|
+
requireCoordinatorMutation(config, "sessions", args);
|
|
1709
|
+
const canonicalCwd = await assertCoordinatorWorkdir(config, args.cwd);
|
|
1710
|
+
const hasTask = typeof args.task === "string" && args.task.trim().length > 0;
|
|
1711
|
+
const hasPrompt = typeof args.prompt === "string" && args.prompt.trim().length > 0;
|
|
1712
|
+
const task = hasTask ? String(args.task) : hasPrompt ? String(args.prompt) : null;
|
|
1713
|
+
if (!task) return { ok: false, reason: "task_required" };
|
|
1714
|
+
const promptAliasIgnored = hasTask && hasPrompt;
|
|
1715
|
+
const mutationRequested = args.allow_mutation === true;
|
|
1716
|
+
const taggedPrompt = workflowPrompt(delegateWorkflow, name, canonicalCwd, task, {
|
|
1717
|
+
mutationRequested,
|
|
1718
|
+
model: typeof args.model === "string" ? args.model : null,
|
|
1719
|
+
});
|
|
1720
|
+
|
|
1721
|
+
let session: Record<string, unknown>;
|
|
1722
|
+
let reusedSession = false;
|
|
1723
|
+
if (args.session_id != null) {
|
|
1724
|
+
const sessionId = safeExternalId("session", args.session_id);
|
|
1725
|
+
const existing = asRecord(await readJsonFile(sessionFile(sessionId)));
|
|
1726
|
+
if (!existing) return { ok: false, reason: "unknown_session", session_id: sessionId };
|
|
1727
|
+
const storedCwd = typeof existing.cwd === "string" ? existing.cwd : null;
|
|
1728
|
+
const canonicalStored = storedCwd ? await canonicalizePath(storedCwd) : null;
|
|
1729
|
+
const canonicalRequested = await canonicalizePath(canonicalCwd);
|
|
1730
|
+
if (!canonicalStored || canonicalStored !== canonicalRequested) {
|
|
1731
|
+
return { ok: false, reason: "session_cwd_mismatch", session_id: sessionId };
|
|
1732
|
+
}
|
|
1733
|
+
session = existing;
|
|
1734
|
+
reusedSession = true;
|
|
1735
|
+
} else {
|
|
1736
|
+
const input = {
|
|
1737
|
+
cwd: canonicalCwd,
|
|
1738
|
+
prompt: undefined,
|
|
1739
|
+
namespace: config.namespace,
|
|
1740
|
+
worktree: true as const,
|
|
1741
|
+
};
|
|
1742
|
+
const started = services.startSession
|
|
1743
|
+
? await services.startSession(input)
|
|
1744
|
+
: await startTmuxSession(config, input, namespaceDir, commandRunner);
|
|
1745
|
+
const startedRecord = asRecord(started);
|
|
1746
|
+
if (!startedRecord) throw new Error("coordinator_session_command_required");
|
|
1747
|
+
session = normalizeSession(startedRecord);
|
|
1748
|
+
await writeJsonFile(sessionFile(session.session_id), session);
|
|
1749
|
+
await appendCoordinatorEvent(namespaceDir, {
|
|
1750
|
+
kind: "session.started",
|
|
1751
|
+
sessionId: String(session.session_id),
|
|
1752
|
+
summary: `Session ${String(session.session_id)} started by coordinator delegate`,
|
|
1753
|
+
payloadRef: path.relative(namespaceDir, sessionFile(session.session_id)),
|
|
1754
|
+
metadata: { delegate: true, workflow: delegateWorkflow },
|
|
1755
|
+
});
|
|
1756
|
+
const live = hasTmuxIdentity(session) ? await hasTmuxSession(session, commandRunner) : null;
|
|
1757
|
+
await writeSessionState(namespaceDir, String(session.session_id), "ready_for_input", {
|
|
1758
|
+
live,
|
|
1759
|
+
reason: null,
|
|
1760
|
+
});
|
|
1761
|
+
}
|
|
1762
|
+
|
|
1763
|
+
const sessionId = String(session.session_id);
|
|
1764
|
+
const activeTurn = reusedSession ? await readActiveTurn(namespaceDir, sessionId) : null;
|
|
1765
|
+
if (activeTurn && args.force !== true && args.queue !== true) {
|
|
1766
|
+
return {
|
|
1767
|
+
ok: false,
|
|
1768
|
+
reason: "active_turn_exists",
|
|
1769
|
+
session_id: sessionId,
|
|
1770
|
+
active_turn_id: activeTurn.turn_id,
|
|
1771
|
+
};
|
|
1772
|
+
}
|
|
1773
|
+
if (activeTurn && args.force === true) {
|
|
1774
|
+
const timestamp = new Date().toISOString();
|
|
1775
|
+
const superseded = {
|
|
1776
|
+
...activeTurn,
|
|
1777
|
+
status: "superseded" as const,
|
|
1778
|
+
updated_at: timestamp,
|
|
1779
|
+
completed_at: timestamp,
|
|
1780
|
+
};
|
|
1781
|
+
await writeTurnRecord(namespaceDir, superseded);
|
|
1782
|
+
await clearActiveTurn(namespaceDir, superseded);
|
|
1783
|
+
}
|
|
1784
|
+
const shouldQueue = args.queue === true && args.force !== true && !!activeTurn;
|
|
1785
|
+
const turn = shouldQueue
|
|
1786
|
+
? makeTurnRecord(config, sessionId, taggedPrompt, "queued")
|
|
1787
|
+
: await activateTurn(session, makeTurnRecord(config, sessionId, taggedPrompt, "active"));
|
|
1788
|
+
if (shouldQueue) await writeTurnRecord(namespaceDir, turn);
|
|
1789
|
+
await appendCoordinatorEvent(namespaceDir, {
|
|
1790
|
+
kind: "delegation.started",
|
|
1791
|
+
sessionId,
|
|
1792
|
+
turnId: turn.turn_id,
|
|
1793
|
+
summary: `Delegated ${delegateWorkflow} via ${name} on session ${sessionId}`,
|
|
1794
|
+
metadata: {
|
|
1795
|
+
workflow: delegateWorkflow,
|
|
1796
|
+
tool_name: name,
|
|
1797
|
+
reused_session: reusedSession,
|
|
1798
|
+
queued: shouldQueue,
|
|
1799
|
+
allow_mutation: args.allow_mutation === true,
|
|
1800
|
+
},
|
|
1801
|
+
});
|
|
1802
|
+
const sessionState = await readSessionState(namespaceDir, sessionId);
|
|
1803
|
+
const base: Record<string, unknown> = {
|
|
1804
|
+
ok: true,
|
|
1805
|
+
workflow: delegateWorkflow,
|
|
1806
|
+
tool_name: name,
|
|
1807
|
+
session_id: sessionId,
|
|
1808
|
+
turn_id: turn.turn_id,
|
|
1809
|
+
active_turn_id: shouldQueue ? activeTurn?.turn_id : turn.turn_id,
|
|
1810
|
+
status: turn.status,
|
|
1811
|
+
queued: turn.delivery.queued,
|
|
1812
|
+
delivered: turn.delivery.delivered,
|
|
1813
|
+
delivery: turn.delivery,
|
|
1814
|
+
session,
|
|
1815
|
+
session_state: sessionState,
|
|
1816
|
+
turn,
|
|
1817
|
+
awaited: false,
|
|
1818
|
+
artifacts: [],
|
|
1819
|
+
};
|
|
1820
|
+
if (promptAliasIgnored) base.prompt_alias_ignored = true;
|
|
1821
|
+
if (args.await_completion === true && !shouldQueue) {
|
|
1822
|
+
const timeoutMs = boundedTimeoutMs(args.timeout_ms);
|
|
1823
|
+
const pollIntervalMs = boundedPollIntervalMs(args.poll_interval_ms);
|
|
1824
|
+
const deadline = Date.now() + timeoutMs;
|
|
1825
|
+
let payload = await readTurnPayload(turn.turn_id, sessionId, args.lines);
|
|
1826
|
+
while (
|
|
1827
|
+
payload.ok === true &&
|
|
1828
|
+
!TERMINAL_TURN_STATUSES.has((payload.turn as TurnRecord).status) &&
|
|
1829
|
+
Date.now() < deadline
|
|
1830
|
+
) {
|
|
1831
|
+
const remainingMs = deadline - Date.now();
|
|
1832
|
+
await waitForTurnStateChange(
|
|
1833
|
+
namespaceDir,
|
|
1834
|
+
payload.turn as TurnRecord,
|
|
1835
|
+
Math.min(pollIntervalMs, remainingMs),
|
|
1836
|
+
);
|
|
1837
|
+
payload = await readTurnPayload(turn.turn_id, sessionId, args.lines);
|
|
1838
|
+
}
|
|
1839
|
+
const awaitedTurn = (payload.ok === true ? payload.turn : turn) as TurnRecord;
|
|
1840
|
+
base.awaited = true;
|
|
1841
|
+
base.status = awaitedTurn.status;
|
|
1842
|
+
base.turn = awaitedTurn;
|
|
1843
|
+
base.final_response = (awaitedTurn as unknown as Record<string, unknown>).final_response ?? null;
|
|
1844
|
+
base.evidence = (awaitedTurn as unknown as Record<string, unknown>).evidence ?? [];
|
|
1845
|
+
if (payload.ok === true) {
|
|
1846
|
+
base.session_state = payload.session_state;
|
|
1847
|
+
base.advisory_status = payload.advisory_status;
|
|
1848
|
+
}
|
|
1849
|
+
// Mirror gjc_coordinator_await_turn timeout semantics: a still-active
|
|
1850
|
+
// turn at the deadline is a bounded timeout, not a completion.
|
|
1851
|
+
if (!TERMINAL_TURN_STATUSES.has(awaitedTurn.status)) {
|
|
1852
|
+
base.timed_out = true;
|
|
1853
|
+
base.reason = "timeout";
|
|
1854
|
+
base.ok = false;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
return base;
|
|
1858
|
+
}
|
|
1590
1859
|
if (name === "gjc_coordinator_start_session") {
|
|
1591
1860
|
requireCoordinatorMutation(config, "sessions", args);
|
|
1592
1861
|
const cwd = await assertCoordinatorWorkdir(config, args.cwd);
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static built-in daemon controller map.
|
|
3
|
+
*
|
|
4
|
+
* Intentionally a static map keyed by daemon kind rather than a mutable plugin
|
|
5
|
+
* registry: there is exactly one kind today (`telegram`). Promote to a richer
|
|
6
|
+
* registry only when a second daemon kind exists.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Settings } from "../config/settings";
|
|
10
|
+
import { type TelegramDaemonControlDeps, TelegramDaemonController } from "../notifications/telegram-daemon-control";
|
|
11
|
+
import type { BuiltInDaemonController, DaemonKind } from "./control-types";
|
|
12
|
+
|
|
13
|
+
export const BUILT_IN_DAEMON_KINDS = ["telegram"] as const satisfies readonly DaemonKind[];
|
|
14
|
+
|
|
15
|
+
export interface BuiltInDaemonControllerDeps {
|
|
16
|
+
telegram?: TelegramDaemonControlDeps;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function createBuiltInDaemonControllers(
|
|
20
|
+
settings: Settings,
|
|
21
|
+
deps: BuiltInDaemonControllerDeps = {},
|
|
22
|
+
): Record<DaemonKind, BuiltInDaemonController> {
|
|
23
|
+
return {
|
|
24
|
+
telegram: new TelegramDaemonController(settings, deps.telegram),
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the controllers a command should act on. `--all` selects every
|
|
30
|
+
* built-in kind; otherwise the explicit `kinds` (defaulting to `telegram`).
|
|
31
|
+
*/
|
|
32
|
+
export function selectDaemonControllers(
|
|
33
|
+
settings: Settings,
|
|
34
|
+
kinds: DaemonKind[] | undefined,
|
|
35
|
+
all: boolean,
|
|
36
|
+
deps: BuiltInDaemonControllerDeps = {},
|
|
37
|
+
): BuiltInDaemonController[] {
|
|
38
|
+
const map = createBuiltInDaemonControllers(settings, deps);
|
|
39
|
+
if (all) return Object.values(map);
|
|
40
|
+
const selected = kinds && kinds.length > 0 ? kinds : (["telegram"] as DaemonKind[]);
|
|
41
|
+
return selected.map(kind => {
|
|
42
|
+
const controller = map[kind];
|
|
43
|
+
if (!controller) throw new Error(`unknown daemon kind: ${kind}`);
|
|
44
|
+
return controller;
|
|
45
|
+
});
|
|
46
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Public types for the `gjc daemon` control plane.
|
|
3
|
+
*
|
|
4
|
+
* Deliberately compact: a small result/status surface plus a built-in
|
|
5
|
+
* controller contract. There is exactly one daemon kind today (`telegram`);
|
|
6
|
+
* a richer registry is intentionally deferred until a second kind exists.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export type DaemonKind = "telegram";
|
|
10
|
+
|
|
11
|
+
export type DaemonAction = "list" | "status" | "stop" | "reload";
|
|
12
|
+
|
|
13
|
+
export type DaemonHealth = "not_configured" | "stopped" | "running" | "stale" | "stopping" | "error";
|
|
14
|
+
|
|
15
|
+
export interface DaemonRuntimeInfo {
|
|
16
|
+
/** `source` when respawn goes through bun/node + the entry script; `compiled` for a single-file binary. */
|
|
17
|
+
mode: "source" | "compiled";
|
|
18
|
+
execPath: string;
|
|
19
|
+
/** True only in source/dev mode, where a respawn loads amended TypeScript directly. */
|
|
20
|
+
reloadPicksUpSourceEdits: boolean;
|
|
21
|
+
/** Present when the runtime mode constrains what reload can achieve (e.g. compiled binary). */
|
|
22
|
+
warning?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface DaemonStatus {
|
|
26
|
+
kind: DaemonKind;
|
|
27
|
+
configured: boolean;
|
|
28
|
+
health: DaemonHealth;
|
|
29
|
+
pid?: number;
|
|
30
|
+
ownerId?: string;
|
|
31
|
+
startedAt?: number;
|
|
32
|
+
heartbeatAt?: number;
|
|
33
|
+
roots?: string[];
|
|
34
|
+
rootCount?: number;
|
|
35
|
+
runtime: DaemonRuntimeInfo;
|
|
36
|
+
detail?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface DaemonOperationOptions {
|
|
40
|
+
/** How long to wait for cooperative release before escalating. */
|
|
41
|
+
gracefulTimeoutMs?: number;
|
|
42
|
+
/** How long to wait for the old pid to die after SIGKILL. */
|
|
43
|
+
killTimeoutMs?: number;
|
|
44
|
+
/** Allow hard-kill escalation / acting on a still-live owner. */
|
|
45
|
+
force?: boolean;
|
|
46
|
+
/** For reload: spawn a fresh owner even when none is currently running. */
|
|
47
|
+
spawnIfStopped?: boolean;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DaemonOperationResult {
|
|
51
|
+
kind: DaemonKind;
|
|
52
|
+
action: Exclude<DaemonAction, "list">;
|
|
53
|
+
ok: boolean;
|
|
54
|
+
before?: DaemonStatus;
|
|
55
|
+
after?: DaemonStatus;
|
|
56
|
+
warnings: string[];
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface BuiltInDaemonController {
|
|
61
|
+
readonly kind: DaemonKind;
|
|
62
|
+
status(): Promise<DaemonStatus>;
|
|
63
|
+
stop(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
|
|
64
|
+
reload(opts?: DaemonOperationOptions): Promise<DaemonOperationResult>;
|
|
65
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared source-vs-compiled runtime detection for daemon spawning.
|
|
3
|
+
*
|
|
4
|
+
* Centralizes the logic previously embedded in `ensureTelegramDaemonRunning`
|
|
5
|
+
* so session autostart, reload, and status reporting agree on how a daemon
|
|
6
|
+
* process is launched and whether a reload can pick up amended source.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
|
|
11
|
+
export interface GjcRuntimeSpawnInfo {
|
|
12
|
+
execPath: string;
|
|
13
|
+
mode: "source" | "compiled";
|
|
14
|
+
/** Prefix prepended before the gjc subcommand args; `[Bun.main]` in source mode, otherwise `[]`. */
|
|
15
|
+
argsPrefix: string[];
|
|
16
|
+
/** True only when respawn loads edited TypeScript directly (source/dev mode). */
|
|
17
|
+
reloadPicksUpSourceEdits: boolean;
|
|
18
|
+
/** Set in compiled mode to explain that a rebuild is required before reload picks up source edits. */
|
|
19
|
+
warning?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const COMPILED_RELOAD_WARNING =
|
|
23
|
+
"Compiled binary: reload respawns the same binary. Rebuild the binary first for amended source to take effect.";
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Resolve how to spawn a detached gjc subcommand for the current runtime.
|
|
27
|
+
*
|
|
28
|
+
* Source/dev mode (bun/node) prepends the entry script (`Bun.main`) so the
|
|
29
|
+
* respawn loads edited source. A compiled single-file binary self-spawns its
|
|
30
|
+
* own subcommand directly and cannot pick up workspace source edits.
|
|
31
|
+
*/
|
|
32
|
+
export function resolveGjcRuntimeSpawnInfo(execPath: string = process.execPath): GjcRuntimeSpawnInfo {
|
|
33
|
+
const base = path.basename(execPath).toLowerCase();
|
|
34
|
+
const fromSource = base === "bun" || base === "node" || base.startsWith("bun") || base.startsWith("node");
|
|
35
|
+
const mainScript = fromSource && typeof Bun !== "undefined" ? (Bun as unknown as { main?: string }).main : undefined;
|
|
36
|
+
if (fromSource) {
|
|
37
|
+
return {
|
|
38
|
+
execPath,
|
|
39
|
+
mode: "source",
|
|
40
|
+
argsPrefix: mainScript ? [mainScript] : [],
|
|
41
|
+
reloadPicksUpSourceEdits: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
return {
|
|
45
|
+
execPath,
|
|
46
|
+
mode: "compiled",
|
|
47
|
+
argsPrefix: [],
|
|
48
|
+
reloadPicksUpSourceEdits: false,
|
|
49
|
+
warning: COMPILED_RELOAD_WARNING,
|
|
50
|
+
};
|
|
51
|
+
}
|