@gajae-code/coding-agent 0.12.0 → 0.12.1
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 +82 -1
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/config/settings-schema.d.ts +54 -0
- package/dist/types/exa/mcp-client.d.ts +10 -1
- package/dist/types/exec/bash-executor.d.ts +45 -12
- package/dist/types/extensibility/extensions/types.d.ts +16 -2
- package/dist/types/extensibility/gjc-plugins/index.d.ts +12 -2
- package/dist/types/extensibility/gjc-plugins/installer.d.ts +63 -7
- package/dist/types/extensibility/gjc-plugins/lifecycle-reconciliation.d.ts +55 -0
- package/dist/types/extensibility/gjc-plugins/lifecycle.d.ts +64 -0
- package/dist/types/extensibility/gjc-plugins/runtime-quarantine.d.ts +67 -0
- package/dist/types/extensibility/gjc-plugins/session-validation.d.ts +3 -1
- package/dist/types/extensibility/gjc-plugins/types.d.ts +124 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +1 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +18 -0
- package/dist/types/gjc-runtime/team-launch.d.ts +1 -1
- package/dist/types/gjc-runtime/team-runtime.d.ts +25 -0
- package/dist/types/gjc-runtime/team-worker-heartbeat.d.ts +43 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +10 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +31 -80
- package/dist/types/modes/acp/acp-agent.d.ts +2 -19
- package/dist/types/modes/components/gjc-bundle-settings.d.ts +34 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +1 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +18 -0
- package/dist/types/modes/components/settings-selector.d.ts +9 -0
- package/dist/types/modes/components/status-line/gh.d.ts +5 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +18 -1
- package/dist/types/reminders/star-reminder.d.ts +3 -13
- package/dist/types/runtime-mcp/manager.d.ts +1 -0
- package/dist/types/sdk/acp/adapter.d.ts +3 -1
- package/dist/types/sdk/broker/transport.d.ts +2 -0
- package/dist/types/sdk/bus/attachment-registry.d.ts +1 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +4 -1
- package/dist/types/sdk/bus/config.d.ts +6 -2
- package/dist/types/sdk/bus/index.d.ts +29 -0
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +8 -1
- package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
- package/dist/types/sdk/bus/reconciliation-store.d.ts +7 -3
- package/dist/types/sdk/bus/rich-draft.d.ts +1 -0
- package/dist/types/sdk/bus/rich-render.d.ts +2 -0
- package/dist/types/sdk/bus/telegram-adoption-intent.d.ts +165 -0
- package/dist/types/sdk/bus/telegram-cli.d.ts +12 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +11 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +25 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +11 -1
- package/dist/types/sdk/bus/threaded-render.d.ts +3 -2
- package/dist/types/sdk/bus/topic-registry.d.ts +11 -1
- package/dist/types/sdk/host/query/revision-store.d.ts +2 -0
- package/dist/types/sdk/host/reverse-leases.d.ts +1 -1
- package/dist/types/sdk/prompt-status.d.ts +23 -3
- package/dist/types/sdk/session.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +38 -2
- package/dist/types/session/artifacts.d.ts +8 -0
- package/dist/types/session/blob-store.d.ts +57 -4
- package/dist/types/session/internal/managed-session-storage.d.ts +8 -1
- package/dist/types/session/session-manager.d.ts +23 -6
- package/dist/types/session/session-storage.d.ts +4 -0
- package/dist/types/session/streaming-output.d.ts +36 -7
- package/dist/types/setup/credential-import.d.ts +12 -0
- package/dist/types/skill-state/active-state.d.ts +12 -0
- package/dist/types/skill-state/workflow-hud.d.ts +6 -0
- package/dist/types/task/executor.d.ts +3 -2
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +7 -1
- package/dist/types/task/types.d.ts +23 -0
- package/dist/types/tools/ask-answer-registry.d.ts +2 -2
- package/dist/types/tools/bash-interactive.d.ts +4 -1
- package/dist/types/tools/bash.d.ts +3 -1
- package/dist/types/tools/image-gen.d.ts +4 -0
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/tools/output-meta.d.ts +23 -2
- package/dist/types/tools/path-utils.d.ts +1 -0
- package/dist/types/tools/read.d.ts +0 -6
- package/dist/types/tools/review.d.ts +5 -7
- package/dist/types/tools/subagent.d.ts +2 -0
- package/dist/types/utils/gh.d.ts +13 -0
- package/dist/types/utils/image-resize.d.ts +1 -0
- package/dist/types/web/search/providers/kimi.d.ts +2 -0
- package/dist/types/web/search/providers/searxng.d.ts +7 -0
- package/dist/types/web/search/providers/xai.d.ts +2 -0
- package/package.json +10 -8
- package/scripts/acp-conformance-agent.ts +218 -0
- package/scripts/capture-gjc-bundle-settings.ts +306 -0
- package/scripts/generate-sdk-operation-inventory.ts +3 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +285 -0
- package/src/async/job-manager.ts +78 -20
- package/src/cli/fast-help.ts +1 -1
- package/src/cli/plugin-cli.ts +176 -18
- package/src/commands/team.ts +19 -1
- package/src/config/file-lock.ts +11 -2
- package/src/config/settings-schema.ts +51 -0
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +19 -16
- package/src/defaults/gjc/skills/team/SKILL.md +2 -0
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +37 -24
- package/src/defaults/gjc/skills/ultragoal/validation-batch-contracts.md +12 -0
- package/src/defaults/gjc-defaults.ts +4 -4
- package/src/exa/mcp-client.ts +12 -3
- package/src/exec/bash-executor.ts +144 -27
- package/src/extensibility/extensions/runner.ts +15 -0
- package/src/extensibility/extensions/types.ts +18 -1
- package/src/extensibility/gjc-plugins/constrained-hooks.ts +8 -2
- package/src/extensibility/gjc-plugins/index.ts +19 -2
- package/src/extensibility/gjc-plugins/installer.ts +189 -50
- package/src/extensibility/gjc-plugins/lifecycle-reconciliation.ts +199 -0
- package/src/extensibility/gjc-plugins/lifecycle.ts +626 -0
- package/src/extensibility/gjc-plugins/observability.ts +3 -3
- package/src/extensibility/gjc-plugins/prompt-appendix.ts +5 -1
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +12 -2
- package/src/extensibility/gjc-plugins/runtime-quarantine.ts +121 -0
- package/src/extensibility/gjc-plugins/schema.ts +84 -12
- package/src/extensibility/gjc-plugins/session-validation.ts +9 -3
- package/src/extensibility/gjc-plugins/types.ts +141 -0
- package/src/extensibility/gjc-plugins/validation.ts +9 -0
- package/src/gjc-runtime/launch-tmux.ts +25 -6
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/ralplan-runtime.ts +411 -17
- package/src/gjc-runtime/team-launch.ts +8 -0
- package/src/gjc-runtime/team-runtime.ts +140 -32
- package/src/gjc-runtime/team-worker-heartbeat.ts +149 -0
- package/src/gjc-runtime/tmux-owner-isolation.ts +10 -0
- package/src/gjc-runtime/tmux-sessions.ts +58 -13
- package/src/gjc-runtime/ultragoal-runtime.ts +401 -611
- package/src/gjc-runtime/workflow-manifest.generated.json +8 -99
- package/src/gjc-runtime/workflow-manifest.ts +4 -68
- package/src/internal-urls/docs-index.generated.ts +15 -15
- package/src/modes/DESIGN.md +51 -0
- package/src/modes/acp/acp-agent.ts +298 -186
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/notifications-settings-editor.ts +12 -1
- package/src/modes/components/oauth-selector.ts +68 -30
- package/src/modes/components/session-observer-overlay.ts +305 -86
- package/src/modes/components/settings-selector.ts +56 -5
- package/src/modes/components/status-line/gh.ts +16 -0
- package/src/modes/components/tool-status-header.ts +3 -14
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +4 -0
- package/src/modes/controllers/extension-ui-controller.ts +7 -0
- package/src/modes/controllers/selector-controller.ts +45 -0
- package/src/modes/runtime-init.ts +3 -0
- package/src/prompts/agent-fragments/ralplan-persistence.md +4 -3
- package/src/prompts/system/eager-todo.md +8 -4
- package/src/prompts/system/system-prompt.md +11 -4
- package/src/prompts/tools/bash.md +5 -5
- package/src/prompts/tools/search-tool-bm25.md +5 -0
- package/src/prompts/tools/task.md +1 -1
- package/src/reminders/star-reminder.ts +4 -42
- package/src/runtime-mcp/manager.ts +19 -4
- package/src/sdk/acp/adapter.ts +22 -6
- package/src/sdk/broker/transport.ts +35 -3
- package/src/sdk/bus/attachment-registry.ts +5 -1
- package/src/sdk/bus/chat-daemon-cli.ts +4 -39
- package/src/sdk/bus/chat-daemon-control.ts +6 -3
- package/src/sdk/bus/config.ts +18 -2
- package/src/sdk/bus/index.ts +661 -126
- package/src/sdk/bus/kind-aware-reconciliation.ts +148 -73
- package/src/sdk/bus/lifecycle-commands.ts +4 -1
- package/src/sdk/bus/lifecycle-control-runtime.ts +403 -88
- package/src/sdk/bus/reconciliation-store.ts +95 -16
- package/src/sdk/bus/rich-draft.ts +1 -1
- package/src/sdk/bus/rich-render.ts +2 -2
- package/src/sdk/bus/telegram-adoption-intent.ts +574 -0
- package/src/sdk/bus/telegram-cli.ts +33 -8
- package/src/sdk/bus/telegram-daemon-cli.ts +1 -0
- package/src/sdk/bus/telegram-daemon-contract.ts +11 -4
- package/src/sdk/bus/telegram-daemon.ts +916 -23
- package/src/sdk/bus/telegram-reference.ts +40 -3
- package/src/sdk/bus/threaded-render.ts +48 -3
- package/src/sdk/bus/topic-registry.ts +20 -0
- package/src/sdk/host/query/revision-store.ts +36 -2
- package/src/sdk/host/reverse-leases.ts +44 -7
- package/src/sdk/prompt-status.ts +16 -3
- package/src/sdk/protocol/operation-inventory.generated.json +22 -0
- package/src/sdk/session.ts +88 -9
- package/src/session/agent-session.ts +822 -172
- package/src/session/artifacts.ts +22 -0
- package/src/session/auth-broker-config.ts +10 -3
- package/src/session/blob-store.ts +915 -24
- package/src/session/internal/managed-session-scope.ts +44 -6
- package/src/session/internal/managed-session-storage.ts +48 -6
- package/src/session/session-manager.ts +1614 -435
- package/src/session/session-storage.ts +9 -1
- package/src/session/streaming-output.ts +238 -68
- package/src/setup/credential-import.ts +75 -13
- package/src/skill-state/active-state.ts +32 -0
- package/src/skill-state/workflow-hud.ts +17 -0
- package/src/skill-state/workflow-mutation-guard.ts +496 -2
- package/src/task/executor.ts +122 -39
- package/src/task/index.ts +23 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +23 -7
- package/src/task/render.ts +6 -2
- package/src/task/types.ts +108 -0
- package/src/tools/ask-answer-registry.ts +12 -6
- package/src/tools/ask.ts +86 -55
- package/src/tools/ast-edit.ts +1 -0
- package/src/tools/ast-grep.ts +1 -0
- package/src/tools/bash-interactive.ts +8 -3
- package/src/tools/bash.ts +503 -60
- package/src/tools/find.ts +1 -0
- package/src/tools/image-gen.ts +29 -5
- package/src/tools/index.ts +4 -0
- package/src/tools/output-meta.ts +108 -14
- package/src/tools/path-utils.ts +3 -2
- package/src/tools/read.ts +18 -0
- package/src/tools/review.ts +5 -7
- package/src/tools/search.ts +1 -0
- package/src/tools/subagent-render.ts +6 -2
- package/src/tools/subagent.ts +6 -0
- package/src/tools/telegram-send.ts +6 -3
- package/src/utils/gh.ts +39 -0
- package/src/utils/image-resize.ts +3 -0
- package/src/web/search/providers/anthropic.ts +4 -4
- package/src/web/search/providers/kimi.ts +15 -5
- package/src/web/search/providers/searxng.ts +47 -4
- package/src/web/search/providers/xai.ts +7 -2
- package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +0 -23
- package/src/defaults/gjc/skills/ultragoal/pipeline-validation-contracts.md +0 -30
- package/src/gjc-runtime/ultragoal-pipeline.ts +0 -306
package/src/cli/plugin-cli.ts
CHANGED
|
@@ -7,7 +7,19 @@
|
|
|
7
7
|
import { APP_NAME, getProjectDir } from "@gajae-code/utils";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import { resolveOrDefaultProjectRegistryPath } from "../discovery/helpers";
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
applyGjcBundleUpdate,
|
|
12
|
+
bundleIdentity,
|
|
13
|
+
type GjcBundleIdentity,
|
|
14
|
+
type GjcBundleSummary,
|
|
15
|
+
GjcPluginLoadError,
|
|
16
|
+
getGjcBundle,
|
|
17
|
+
installGjcBundle,
|
|
18
|
+
isGjcPluginBundleSource,
|
|
19
|
+
isGjcPluginSourceShape,
|
|
20
|
+
listGjcBundles,
|
|
21
|
+
previewGjcBundleUpdate,
|
|
22
|
+
} from "../extensibility/gjc-plugins";
|
|
11
23
|
import { PluginManager, parseSettingValue, validateSetting } from "../extensibility/plugins";
|
|
12
24
|
import {
|
|
13
25
|
getInstalledPluginsRegistryPath,
|
|
@@ -312,9 +324,118 @@ async function handleDiscover(args: string[], _flags: PluginCommandArgs["flags"]
|
|
|
312
324
|
}
|
|
313
325
|
}
|
|
314
326
|
|
|
327
|
+
/**
|
|
328
|
+
* Scope-qualified GJC bundle upgrade: re-resolve the stored source, review the
|
|
329
|
+
* candidate, then apply it as a compare-and-swap. `--dry-run` stops after the
|
|
330
|
+
* preview. Requires exactly one of `--user` / `--project` because (scope, name)
|
|
331
|
+
* is the canonical target.
|
|
332
|
+
*/
|
|
333
|
+
async function handleGjcUpgrade(name: string, flags: PluginCommandArgs["flags"]): Promise<void> {
|
|
334
|
+
if (flags.user === flags.project) {
|
|
335
|
+
console.error(chalk.red(`GJC bundle upgrade requires exactly one of --user or --project for "${name}".`));
|
|
336
|
+
process.exit(1);
|
|
337
|
+
}
|
|
338
|
+
const scope: "user" | "project" = flags.user ? "user" : "project";
|
|
339
|
+
const ctx = { cwd: getProjectDir() };
|
|
340
|
+
const identity = bundleIdentity(scope, name);
|
|
341
|
+
|
|
342
|
+
const emitError = (error: { code: string; message: string; recovery?: string }): never => {
|
|
343
|
+
if (flags.json) console.log(JSON.stringify({ error }, null, 2));
|
|
344
|
+
else {
|
|
345
|
+
console.error(chalk.red(`${theme.status.error} ${error.message}`));
|
|
346
|
+
if (error.recovery) console.error(chalk.dim(` Try: ${error.recovery}`));
|
|
347
|
+
}
|
|
348
|
+
process.exit(3);
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
// Source re-resolution can throw with a cause carrying the raw locator, so
|
|
352
|
+
// the whole flow reports a stable code instead of the underlying error.
|
|
353
|
+
try {
|
|
354
|
+
await runGjcUpgrade(ctx, identity, name, scope, flags, emitError);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
const reason = err instanceof GjcPluginLoadError ? err.code : "upgrade_failed";
|
|
357
|
+
console.error(chalk.red(`${theme.status.error} Failed to upgrade GJC bundle ${name} (${reason})`));
|
|
358
|
+
process.exit(1);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
async function runGjcUpgrade(
|
|
363
|
+
ctx: { cwd: string },
|
|
364
|
+
identity: GjcBundleIdentity,
|
|
365
|
+
name: string,
|
|
366
|
+
scope: "user" | "project",
|
|
367
|
+
flags: PluginCommandArgs["flags"],
|
|
368
|
+
emitError: (error: { code: string; message: string; recovery?: string }) => never,
|
|
369
|
+
): Promise<void> {
|
|
370
|
+
const preview = await previewGjcBundleUpdate(ctx, identity);
|
|
371
|
+
if (!preview.ok) emitError(preview.error);
|
|
372
|
+
else if (flags.dryRun || !preview.value.changed) {
|
|
373
|
+
const { changed, candidateVersion, addedSurfaceIds, removedSurfaceIds } = preview.value;
|
|
374
|
+
if (flags.json) {
|
|
375
|
+
console.log(
|
|
376
|
+
JSON.stringify(
|
|
377
|
+
{
|
|
378
|
+
status: changed ? "update-available" : "up-to-date",
|
|
379
|
+
identity,
|
|
380
|
+
currentVersion: preview.value.current.version,
|
|
381
|
+
candidateVersion,
|
|
382
|
+
addedSurfaceIds,
|
|
383
|
+
removedSurfaceIds,
|
|
384
|
+
},
|
|
385
|
+
null,
|
|
386
|
+
2,
|
|
387
|
+
),
|
|
388
|
+
);
|
|
389
|
+
} else if (!changed) {
|
|
390
|
+
console.log(chalk.dim(`GJC bundle ${name} (${scope}) is up to date at ${preview.value.current.version}`));
|
|
391
|
+
} else {
|
|
392
|
+
console.log(
|
|
393
|
+
chalk.cyan(`[dry-run] ${name} (${scope}): ${preview.value.current.version} -> ${candidateVersion}`),
|
|
394
|
+
);
|
|
395
|
+
if (addedSurfaceIds.length > 0) console.log(chalk.dim(` + ${addedSurfaceIds.join(", ")}`));
|
|
396
|
+
if (removedSurfaceIds.length > 0) console.log(chalk.dim(` - ${removedSurfaceIds.join(", ")}`));
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
const applied = await applyGjcBundleUpdate(ctx, preview.value.token);
|
|
400
|
+
if (!applied.ok) emitError(applied.error);
|
|
401
|
+
else if (flags.json) {
|
|
402
|
+
console.log(
|
|
403
|
+
JSON.stringify(
|
|
404
|
+
{
|
|
405
|
+
status: applied.value.status,
|
|
406
|
+
bundle: applied.value.summary,
|
|
407
|
+
remnantCount: applied.value.remnantCount,
|
|
408
|
+
},
|
|
409
|
+
null,
|
|
410
|
+
2,
|
|
411
|
+
),
|
|
412
|
+
);
|
|
413
|
+
} else {
|
|
414
|
+
console.log(
|
|
415
|
+
chalk.green(
|
|
416
|
+
`${theme.status.success} ${applied.value.status} GJC bundle ${name}@${applied.value.summary.version} (${scope})`,
|
|
417
|
+
),
|
|
418
|
+
);
|
|
419
|
+
if (applied.value.remnantCount > 0) {
|
|
420
|
+
console.error(chalk.yellow(` ${applied.value.remnantCount} leftover directory could not be removed`));
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
|
|
315
426
|
async function handleUpgrade(args: string[], flags: PluginCommandArgs["flags"]): Promise<void> {
|
|
316
|
-
const manager = await makeMarketplaceManager();
|
|
317
427
|
const pluginId = args[0];
|
|
428
|
+
// Scope-qualified GJC bundles upgrade through the lifecycle service, never
|
|
429
|
+
// through the marketplace manager.
|
|
430
|
+
if (pluginId && (flags.user || flags.project)) {
|
|
431
|
+
const scope: "user" | "project" = flags.user ? "user" : "project";
|
|
432
|
+
const existing = await getGjcBundle({ cwd: getProjectDir() }, bundleIdentity(scope, pluginId));
|
|
433
|
+
if (existing.ok) {
|
|
434
|
+
await handleGjcUpgrade(pluginId, flags);
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
const manager = await makeMarketplaceManager();
|
|
318
439
|
try {
|
|
319
440
|
if (pluginId) {
|
|
320
441
|
if (flags.scope) {
|
|
@@ -349,6 +470,15 @@ async function handleUpgrade(args: string[], flags: PluginCommandArgs["flags"]):
|
|
|
349
470
|
}
|
|
350
471
|
}
|
|
351
472
|
|
|
473
|
+
/**
|
|
474
|
+
* Stable, non-identifying reason for an install failure. The raw spec and the
|
|
475
|
+
* underlying cause can both carry credentials, a query string, or an absolute
|
|
476
|
+
* home path, so neither is ever printed.
|
|
477
|
+
*/
|
|
478
|
+
function describeInstallFailure(error: unknown): string {
|
|
479
|
+
return error instanceof GjcPluginLoadError ? error.code : "install_failed";
|
|
480
|
+
}
|
|
481
|
+
|
|
352
482
|
async function handleInstall(
|
|
353
483
|
manager: PluginManager,
|
|
354
484
|
packages: string[],
|
|
@@ -374,27 +504,52 @@ async function handleInstall(
|
|
|
374
504
|
const knownMarketplaces = new Set((await mktMgr.listMarketplaces()).map(m => m.name));
|
|
375
505
|
|
|
376
506
|
for (const spec of packages) {
|
|
377
|
-
// GJC
|
|
378
|
-
// git
|
|
379
|
-
|
|
507
|
+
// A GJC bundle is identified by the SHAPE of its source: a filesystem path,
|
|
508
|
+
// a git locator, or a tarball. npm and marketplace specs are never any of
|
|
509
|
+
// those, so shape alone separates the two worlds without resolving.
|
|
510
|
+
//
|
|
511
|
+
// Shape is checked BEFORE `isGjcPluginBundleSource`, which resolves the
|
|
512
|
+
// source: a deleted or unreachable GJC source fails that probe and would
|
|
513
|
+
// otherwise fall through to npm, losing the create-only refusal the
|
|
514
|
+
// lifecycle owes for an already-installed target.
|
|
515
|
+
if (isGjcPluginSourceShape(spec) || (await isGjcPluginBundleSource(spec))) {
|
|
380
516
|
if (flags.user === flags.project) {
|
|
381
517
|
console.error(
|
|
382
|
-
|
|
518
|
+
// The spec can carry credentials or an absolute home path, so name
|
|
519
|
+
// the missing flag instead of echoing it back.
|
|
520
|
+
chalk.red("GJC plugin bundle install requires exactly one of --user or --project."),
|
|
383
521
|
);
|
|
384
522
|
process.exit(1);
|
|
385
523
|
}
|
|
386
524
|
const scope: "user" | "project" = flags.user ? "user" : "project";
|
|
387
525
|
try {
|
|
388
|
-
const res = await
|
|
526
|
+
const res = await installGjcBundle({ cwd: getProjectDir() }, scope, spec);
|
|
527
|
+
if (!res.ok) {
|
|
528
|
+
const doc = {
|
|
529
|
+
error: { code: res.error.code, message: res.error.message, recovery: res.error.recovery },
|
|
530
|
+
};
|
|
531
|
+
if (flags.json) console.log(JSON.stringify(doc, null, 2));
|
|
532
|
+
else {
|
|
533
|
+
console.error(chalk.red(`${theme.status.error} ${res.error.message}`));
|
|
534
|
+
if (res.error.recovery) console.error(chalk.dim(` Try: ${res.error.recovery}`));
|
|
535
|
+
}
|
|
536
|
+
process.exit(3);
|
|
537
|
+
}
|
|
538
|
+
const { summary } = res.value;
|
|
389
539
|
if (flags.json) {
|
|
390
|
-
console.log(JSON.stringify({
|
|
540
|
+
console.log(JSON.stringify({ status: res.value.status, bundle: summary }, null, 2));
|
|
391
541
|
} else {
|
|
392
542
|
console.log(
|
|
393
|
-
chalk.green(
|
|
543
|
+
chalk.green(
|
|
544
|
+
`${theme.status.success} installed GJC plugin ${summary.identity.name}@${summary.version} (${scope})`,
|
|
545
|
+
),
|
|
394
546
|
);
|
|
395
547
|
}
|
|
396
548
|
} catch (err) {
|
|
397
|
-
|
|
549
|
+
// Never echo the raw spec or the underlying cause: either can carry
|
|
550
|
+
// credentials, a query string, or an absolute home path.
|
|
551
|
+
const reason = err instanceof GjcPluginLoadError ? err.code : "install_failed";
|
|
552
|
+
console.error(chalk.red(`${theme.status.error} Failed to install GJC bundle (${reason})`));
|
|
398
553
|
process.exit(1);
|
|
399
554
|
}
|
|
400
555
|
continue;
|
|
@@ -414,7 +569,9 @@ async function handleInstall(
|
|
|
414
569
|
),
|
|
415
570
|
);
|
|
416
571
|
} catch (err) {
|
|
417
|
-
|
|
572
|
+
// The spec can carry credentials, a query string, or an absolute home
|
|
573
|
+
// path, so report the failure without echoing it or the raw cause.
|
|
574
|
+
console.error(chalk.red(`${theme.status.error} Failed to install plugin (${describeInstallFailure(err)})`));
|
|
418
575
|
process.exit(1);
|
|
419
576
|
}
|
|
420
577
|
continue;
|
|
@@ -449,7 +606,9 @@ async function handleInstall(
|
|
|
449
606
|
}
|
|
450
607
|
}
|
|
451
608
|
} catch (err) {
|
|
452
|
-
|
|
609
|
+
// The spec can carry credentials, a query string, or an absolute home
|
|
610
|
+
// path, so report the failure without echoing it or the raw cause.
|
|
611
|
+
console.error(chalk.red(`${theme.status.error} Failed to install plugin (${describeInstallFailure(err)})`));
|
|
453
612
|
process.exit(1);
|
|
454
613
|
}
|
|
455
614
|
}
|
|
@@ -503,8 +662,7 @@ async function handleList(manager: PluginManager, flags: { json?: boolean }): Pr
|
|
|
503
662
|
const mktMgr = await makeMarketplaceManager();
|
|
504
663
|
const mktPlugins = await mktMgr.listInstalledPlugins();
|
|
505
664
|
const cwd = getProjectDir();
|
|
506
|
-
const [
|
|
507
|
-
const gjcBundles = [...gjcUser.plugins, ...gjcProject.plugins];
|
|
665
|
+
const gjcBundles: GjcBundleSummary[] = await listGjcBundles({ cwd });
|
|
508
666
|
|
|
509
667
|
if (flags.json) {
|
|
510
668
|
console.log(JSON.stringify({ npm: npmPlugins, marketplace: mktPlugins, gjc: gjcBundles }, null, 2));
|
|
@@ -559,9 +717,9 @@ async function handleList(manager: PluginManager, flags: { json?: boolean }): Pr
|
|
|
559
717
|
console.log(chalk.bold("GJC Plugin Bundles:\n"));
|
|
560
718
|
for (const plugin of gjcBundles) {
|
|
561
719
|
const status = plugin.enabled ? chalk.green(theme.status.enabled) : chalk.dim(theme.status.disabled);
|
|
562
|
-
const scopeLabel = chalk.dim(` (${plugin.scope})`);
|
|
563
|
-
const disabledCount = plugin.
|
|
564
|
-
const quarantineCount = plugin.
|
|
720
|
+
const scopeLabel = chalk.dim(` (${plugin.identity.scope})`);
|
|
721
|
+
const disabledCount = plugin.surfaces.filter(s => !s.enabled).length;
|
|
722
|
+
const quarantineCount = plugin.surfaces.filter(s => s.quarantined).length;
|
|
565
723
|
const detail = [
|
|
566
724
|
disabledCount > 0 ? `${disabledCount} disabled` : null,
|
|
567
725
|
quarantineCount > 0 ? `${quarantineCount} quarantined` : null,
|
|
@@ -569,7 +727,7 @@ async function handleList(manager: PluginManager, flags: { json?: boolean }): Pr
|
|
|
569
727
|
.filter((v): v is string => Boolean(v))
|
|
570
728
|
.join(", ");
|
|
571
729
|
console.log(
|
|
572
|
-
`${status} ${plugin.name}@${plugin.version}${scopeLabel}${detail ? chalk.dim(` — ${detail}`) : ""}`,
|
|
730
|
+
`${status} ${plugin.identity.name}@${plugin.version}${scopeLabel}${detail ? chalk.dim(` — ${detail}`) : ""}`,
|
|
573
731
|
);
|
|
574
732
|
}
|
|
575
733
|
}
|
package/src/commands/team.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
readGjcTeamSnapshot,
|
|
14
14
|
shutdownGjcTeam,
|
|
15
15
|
startGjcTeam,
|
|
16
|
+
UnknownGjcTeamApiOperationError,
|
|
16
17
|
} from "../gjc-runtime/team-runtime";
|
|
17
18
|
import { syncSkillActiveState } from "../skill-state/active-state";
|
|
18
19
|
|
|
@@ -183,7 +184,24 @@ export default class Team extends Command {
|
|
|
183
184
|
return;
|
|
184
185
|
}
|
|
185
186
|
const input = parseInputFlag(rest);
|
|
186
|
-
|
|
187
|
+
let result: unknown;
|
|
188
|
+
try {
|
|
189
|
+
result = await executeGjcTeamApiOperation(operation, input);
|
|
190
|
+
} catch (error) {
|
|
191
|
+
if (!(error instanceof UnknownGjcTeamApiOperationError)) throw error;
|
|
192
|
+
process.exitCode = 1;
|
|
193
|
+
if (json) {
|
|
194
|
+
writeReceipt({
|
|
195
|
+
ok: false,
|
|
196
|
+
error: error.code,
|
|
197
|
+
operation: error.operation,
|
|
198
|
+
suggestions: error.suggestions,
|
|
199
|
+
});
|
|
200
|
+
} else {
|
|
201
|
+
process.stderr.write(`${error.message}\n`);
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
187
205
|
const teamName = String(input.team_name ?? input.teamName ?? "").trim();
|
|
188
206
|
if (teamName) {
|
|
189
207
|
try {
|
package/src/config/file-lock.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Stats } from "node:fs";
|
|
2
2
|
import * as fs from "node:fs/promises";
|
|
3
3
|
import * as path from "node:path";
|
|
4
|
-
import { isEnoent } from "@gajae-code/utils/fs-error";
|
|
4
|
+
import { hasFsCode, isEnoent } from "@gajae-code/utils/fs-error";
|
|
5
5
|
|
|
6
6
|
export interface FileLockOptions {
|
|
7
7
|
staleMs?: number;
|
|
@@ -185,7 +185,16 @@ async function staleLockSnapshot(
|
|
|
185
185
|
staleMs: number,
|
|
186
186
|
startTimeCache?: Map<string, string | null>,
|
|
187
187
|
): Promise<LockStaleSnapshot> {
|
|
188
|
-
|
|
188
|
+
let info: LockInfo | null;
|
|
189
|
+
try {
|
|
190
|
+
info = await readLockInfo(lockPath);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
// Windows can transiently deny reads of a just-created lock metadata file
|
|
193
|
+
// while another contender is publishing it. Treat that as active
|
|
194
|
+
// contention and retry rather than failing the caller or reaping by path.
|
|
195
|
+
if (hasFsCode(error, "EPERM")) return { stale: false };
|
|
196
|
+
throw error;
|
|
197
|
+
}
|
|
189
198
|
if (!info) {
|
|
190
199
|
try {
|
|
191
200
|
const stats = await fs.stat(lockPath);
|
|
@@ -275,6 +275,13 @@ export const SETTINGS_SCHEMA = {
|
|
|
275
275
|
values: ["copy-retain", "disabled"] as const,
|
|
276
276
|
default: "copy-retain",
|
|
277
277
|
},
|
|
278
|
+
// SDK-owned prompt deadline. Hidden from the UI; ACP has no separate timeout.
|
|
279
|
+
"sdk.promptDeadlineMs": {
|
|
280
|
+
type: "number",
|
|
281
|
+
default: 1_800_000,
|
|
282
|
+
description: "SDK-owned prompt deadline; ACP has no separate timeout.",
|
|
283
|
+
validate: (value: number) => Number.isSafeInteger(value) && value >= 60_000 && value <= 86_400_000,
|
|
284
|
+
},
|
|
278
285
|
|
|
279
286
|
// Notifications (shared daemon with Telegram/Discord/Slack presentation adapters)
|
|
280
287
|
"notifications.enabled": { type: "boolean", default: false },
|
|
@@ -287,6 +294,17 @@ export const SETTINGS_SCHEMA = {
|
|
|
287
294
|
"notifications.telegram.activation": { type: "record", default: {} as Record<string, unknown> },
|
|
288
295
|
"notifications.telegram.btw.enabled": { type: "boolean", default: true },
|
|
289
296
|
"notifications.telegram.streaming.enabled": { type: "boolean", default: true },
|
|
297
|
+
"notifications.telegram.sound": {
|
|
298
|
+
type: "enum",
|
|
299
|
+
values: ["all", "important", "none"] as const,
|
|
300
|
+
default: "all",
|
|
301
|
+
ui: {
|
|
302
|
+
tab: "notifications",
|
|
303
|
+
label: "Telegram Notification Sounds",
|
|
304
|
+
description: "Choose which Telegram notifications play a sound.",
|
|
305
|
+
editing: "notification-atomic",
|
|
306
|
+
},
|
|
307
|
+
},
|
|
290
308
|
"notifications.telegram.rich.enabled": {
|
|
291
309
|
type: "boolean",
|
|
292
310
|
default: true,
|
|
@@ -489,6 +507,22 @@ export const SETTINGS_SCHEMA = {
|
|
|
489
507
|
options: "runtime",
|
|
490
508
|
},
|
|
491
509
|
},
|
|
510
|
+
"session.resumeModelBehavior": {
|
|
511
|
+
type: "enum",
|
|
512
|
+
values: ["keepSessionModel", "useCurrentDefault", "ask"] as const,
|
|
513
|
+
default: "keepSessionModel",
|
|
514
|
+
ui: {
|
|
515
|
+
tab: "model",
|
|
516
|
+
label: "Resume Model Behavior",
|
|
517
|
+
description:
|
|
518
|
+
"When resuming a session: keep the model that session last used, switch to the currently configured default model, or ask (TUI only; falls back to keeping the session's model in headless/CLI resume).",
|
|
519
|
+
options: [
|
|
520
|
+
{ value: "keepSessionModel", label: "Keep session's saved model" },
|
|
521
|
+
{ value: "useCurrentDefault", label: "Use current default model" },
|
|
522
|
+
{ value: "ask", label: "Ask on resume (TUI only)" },
|
|
523
|
+
],
|
|
524
|
+
},
|
|
525
|
+
},
|
|
492
526
|
|
|
493
527
|
modelTags: { type: "record", default: EMPTY_MODEL_TAGS_RECORD },
|
|
494
528
|
|
|
@@ -501,6 +535,11 @@ export const SETTINGS_SCHEMA = {
|
|
|
501
535
|
default: 0.05,
|
|
502
536
|
validate: (value: number) => Number.isFinite(value) && value > 0 && value <= 1,
|
|
503
537
|
},
|
|
538
|
+
"gjc.ralplan.autoHandoff": {
|
|
539
|
+
type: "enum",
|
|
540
|
+
values: ["off", "ultragoal", "team"],
|
|
541
|
+
default: "off",
|
|
542
|
+
},
|
|
504
543
|
"gjc.ralplan.maxIterations": {
|
|
505
544
|
type: "number",
|
|
506
545
|
default: 5,
|
|
@@ -1224,6 +1263,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
1224
1263
|
"Maximum provider stream replay retries for replay-safe transient stream failures. Counts retries, not the first attempt. Set to 0 to disable provider stream retries.",
|
|
1225
1264
|
},
|
|
1226
1265
|
},
|
|
1266
|
+
"retry.streamFirstEventTimeoutMs": {
|
|
1267
|
+
type: "number",
|
|
1268
|
+
default: 100_000,
|
|
1269
|
+
validate: (value: number) => Number.isFinite(value) && value >= 0,
|
|
1270
|
+
ui: {
|
|
1271
|
+
tab: "model",
|
|
1272
|
+
label: "First Event Timeout",
|
|
1273
|
+
description: "Maximum wait for the first provider stream event, in ms. Set to 0 to disable the watchdog.",
|
|
1274
|
+
},
|
|
1275
|
+
},
|
|
1227
1276
|
"retry.fallbackChains": { type: "record", default: {} as Record<string, string[]> },
|
|
1228
1277
|
"retry.fallbackRevertPolicy": {
|
|
1229
1278
|
type: "enum",
|
|
@@ -3810,6 +3859,7 @@ export interface RetrySettings {
|
|
|
3810
3859
|
maxDelayMs: number;
|
|
3811
3860
|
requestMaxRetries: number;
|
|
3812
3861
|
streamMaxRetries: number;
|
|
3862
|
+
streamFirstEventTimeoutMs: number;
|
|
3813
3863
|
}
|
|
3814
3864
|
|
|
3815
3865
|
export interface MemoriesSettings {
|
|
@@ -3929,6 +3979,7 @@ export interface NotificationsSettings {
|
|
|
3929
3979
|
telegram: {
|
|
3930
3980
|
botToken: string | undefined;
|
|
3931
3981
|
chatId: string | undefined;
|
|
3982
|
+
sound: "all" | "important" | "none";
|
|
3932
3983
|
btw: {
|
|
3933
3984
|
enabled: boolean;
|
|
3934
3985
|
};
|
|
@@ -147,7 +147,7 @@ function askSchema(labels: string[], multi: boolean, allowEmpty: boolean): JsonS
|
|
|
147
147
|
}
|
|
148
148
|
export function decodeAskGateV1(gate: WorkflowGate): PrivateAskGateCodecV1 | null {
|
|
149
149
|
if (
|
|
150
|
-
gate.stage !== "deep-interview" ||
|
|
150
|
+
(gate.stage !== "deep-interview" && gate.stage !== "ralplan" && gate.stage !== "ultragoal") ||
|
|
151
151
|
gate.kind !== "question" ||
|
|
152
152
|
!isRecord(gate.context) ||
|
|
153
153
|
!boundedText(gate.context.prompt, MAX_TEXT) ||
|
|
@@ -859,7 +859,7 @@ interface RuntimePromptAcknowledgement {
|
|
|
859
859
|
turn_id: string;
|
|
860
860
|
}
|
|
861
861
|
|
|
862
|
-
function
|
|
862
|
+
function sdkResultPayload(result: unknown): Record<string, unknown> | null {
|
|
863
863
|
const response = asRecord(result);
|
|
864
864
|
if (!response) return null;
|
|
865
865
|
const envelope = ["ok", "result", "error"].some(key => Object.hasOwn(response, key));
|
|
@@ -887,7 +887,7 @@ function runtimeAcknowledgementIdentity(
|
|
|
887
887
|
}
|
|
888
888
|
|
|
889
889
|
function normalizeRuntimePromptAcknowledgement(result: unknown): RuntimePromptAcknowledgement {
|
|
890
|
-
const acknowledgement =
|
|
890
|
+
const acknowledgement = sdkResultPayload(result);
|
|
891
891
|
if (acknowledgement?.accepted !== true)
|
|
892
892
|
throw new SdkClientError("unavailable", "SDK did not acknowledge prompt delivery.");
|
|
893
893
|
return {
|
|
@@ -2458,15 +2458,8 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
2458
2458
|
items.push(...pageItems);
|
|
2459
2459
|
if (complete) {
|
|
2460
2460
|
const valid = items.every(item => {
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
const gate = item as WorkflowGateQueryRecord & WorkflowGate;
|
|
2464
|
-
return (
|
|
2465
|
-
gate.tag === "pending" &&
|
|
2466
|
-
typeof gate.gate_id === "string" &&
|
|
2467
|
-
gate.gate_id.length > 0 &&
|
|
2468
|
-
!!decodeAskGateV1(gate)
|
|
2469
|
-
);
|
|
2461
|
+
const encoded = JSON.stringify(item);
|
|
2462
|
+
return typeof encoded === "string" && Buffer.byteLength(encoded) <= 16 * 1024;
|
|
2470
2463
|
});
|
|
2471
2464
|
return valid
|
|
2472
2465
|
? { items, revision, complete: true, reason: null }
|
|
@@ -4492,7 +4485,7 @@ export function createCoordinatorMcpServer(options: CoordinatorMcpServerOptions
|
|
|
4492
4485
|
{ id: gateId, response: translated, expectedSessionId: sessionId },
|
|
4493
4486
|
(claimed as { requestId: string }).requestId,
|
|
4494
4487
|
);
|
|
4495
|
-
const resolution =
|
|
4488
|
+
const resolution = sdkResultPayload(result);
|
|
4496
4489
|
const status = resolution?.status;
|
|
4497
4490
|
if (status === "rejected") {
|
|
4498
4491
|
await withAdmittedSessionTransaction(questionPaths, sessionId, async transaction => {
|
|
@@ -180,6 +180,7 @@ Run this phase only when the active deep-interview state or invocation indicates
|
|
|
180
180
|
- Direct `.gjc/` file edits are forbidden unless an explicit force override is active; do not use `write`, `edit`, or `ast_edit` against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or other `.gjc/` paths during normal workflow operation.
|
|
181
181
|
- Preferred: pass the spec markdown **inline** to the native deep-interview write command (`--write … --spec "<markdown>"`) — no scratch file is needed. The CLI is the only sanctioned writer for `.gjc/_session-{sessionid}/specs`.
|
|
182
182
|
- Only if a spec is too large to pass inline, stage it with the `write` tool to a system temp directory (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) outside the project tree, then pass that path to `--spec`. The planning phase-boundary block tolerates these neutral temp writes; never stage interview artifacts inside the repo or under `.gjc/`, and do not improvise repo-relative scratch files.
|
|
183
|
+
- When staging via bash instead of the `write` tool, a heredoc into a neutral temp path (`cat > /tmp/spec.md <<'EOF' … EOF`) is also tolerated; quote the heredoc delimiter (`<<'EOF'`) so the document body stays inert. Never stage into the repo or `.gjc/`, and prefer the `write` tool over bash for large bodies.
|
|
183
184
|
|
|
184
185
|
4. **Initialize state** via `gjc deep-interview write --input '<json>'`:
|
|
185
186
|
|
|
@@ -19,9 +19,10 @@ Ralplan is the consensus planning workflow. It triggers iterative planning with
|
|
|
19
19
|
|
|
20
20
|
## Flags
|
|
21
21
|
|
|
22
|
-
- `--interactive`: Adds draft-review prompts and one-at-a-time reconciliation
|
|
22
|
+
- `--interactive`: Adds draft-review prompts and one-at-a-time reconciliation. When the final receipt resolves `auto_handoff.effectiveTarget` to `off` without `degradationReason: "planning_stuck"`, final approval uses an `ask` workflow gate; a configured automatic admission is handled by step 8.
|
|
23
23
|
- `--deliberate`: Forces high-risk deliberation: pre-mortem plus expanded test planning. It may also auto-enable for explicit auth/security, migration, destructive, incident, compliance/PII, or public-API-breakage risk.
|
|
24
24
|
- `--architect openai-code` / `--critic openai-code`: Use OpenAI code for that review pass when available; otherwise note the fallback and use default GJC review.
|
|
25
|
+
- `gjc.ralplan.autoHandoff`: Selects final-plan admission: `off` (default), `ultragoal`, or `team`. A `team` target degrades to `off` when tmux is unavailable or no current tmux session is usable; the final receipt reports the `team_unavailable:<reason>` degradation. `PLANNING-STUCK` also resolves every target to `off`. Invalid settings reject the final write before any final artifact is persisted. The final receipt's ledger-backed runtime-owned `auto_handoff.effectiveTarget` is authoritative across state loss and run switching.
|
|
25
26
|
- `--write --stage <type> --stage_n <N> --artifact <markdown file path or markdown string>`: Native writer for Planner/Architect/Critic/revision/ADR/final pending-approval markdown under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/`; do not edit `.gjc/` directly.
|
|
26
27
|
|
|
27
28
|
## Corrupt current-session state recovery
|
|
@@ -32,24 +33,26 @@ For corrupt, tampered, unreadable, or stale current-session ralplan state, run `
|
|
|
32
33
|
|
|
33
34
|
## Planning/Execution Boundary
|
|
34
35
|
|
|
35
|
-
Ralplan is planning only. It may inspect context and draft plan/spec/proposal artifacts, but those remain `pending approval` until explicit current-turn or structured-UI execution approval. Before
|
|
36
|
+
Ralplan is planning only. It may inspect context and draft plan/spec/proposal artifacts, but those remain `pending approval` until explicit current-turn or structured-UI execution approval, or a valid non-off final receipt's runtime-owned `auto_handoff.effectiveTarget` admits the existing handoff chain. Before either admission, do not mutate product source, run mutation-oriented shell, commit, push, open PRs, invoke execution skills, or delegate implementation.
|
|
36
37
|
|
|
37
|
-
|
|
38
|
+
Except for a terminal `planning_stuck` final receipt, explicitly naming `ultragoal` or `team` (including `/skill:` and `gjc` forms) counts as opting into execution for that skill — do not re-ask for the same consent.
|
|
38
39
|
|
|
39
40
|
Persist planning artifacts and handoffs through the ralplan CLI writer, never direct `.gjc/` edits:
|
|
40
41
|
Direct `write`, `edit`, or `ast_edit` calls against `.gjc/_session-{sessionid}/specs`, `.gjc/_session-{sessionid}/plans`, `.gjc/_session-{sessionid}/state`, or any other `.gjc/` path are forbidden unless an explicit force override is active.
|
|
41
42
|
|
|
42
43
|
```bash
|
|
43
|
-
gjc ralplan --write --stage <type> --stage_n <N> --artifact "markdown file path or markdown string"
|
|
44
|
+
gjc ralplan --write --session-id <owner-session-id> --run-id <run-id> --stage <type> --stage_n <N> --artifact "markdown file path or markdown string"
|
|
44
45
|
# restricted role agents use:
|
|
45
|
-
gjc ralplan --write --stage <type> --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT
|
|
46
|
+
gjc ralplan --write --session-id <owner-session-id> --run-id <run-id> --stage <type> --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT
|
|
46
47
|
```
|
|
47
48
|
|
|
48
|
-
Use stages `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`; increment `--stage_n` each consensus pass. The writer accepts inline markdown, an artifact path prepared outside `.gjc/`, or `--artifact-env GJC_RALPLAN_ARTIFACT`, persists `stage-<NN>-<stage>.md` plus `index.jsonl` under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/`, and copies `final` to `pending-approval.md`. Ralplan mutation blocking is enforced in code; use temp directories (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) only for oversized scratch artifacts, never the repo or `.gjc/`.
|
|
49
|
+
Use stages `planner`, `architect`, `critic`, `revision`, `post-interview`, `adr`, or `final`; increment `--stage_n` each consensus pass. The writer accepts inline markdown, an artifact path prepared outside `.gjc/`, or `--artifact-env GJC_RALPLAN_ARTIFACT`, persists `stage-<NN>-<stage>.md` plus `index.jsonl` under `.gjc/_session-{sessionid}/plans/ralplan/<run-id>/`, and copies `final` to `pending-approval.md`. Ralplan mutation blocking is enforced in code; use temp directories (`os.tmpdir()`/`$TMPDIR`, `/tmp`, `/var/tmp`) only for oversized scratch artifacts, never the repo or `.gjc/`. Staging via the `write` tool or a quoted-delimiter bash heredoc (`cat > /tmp/plan.md <<'EOF' … EOF`) into those temp roots is tolerated by the planning-phase guard.
|
|
49
50
|
|
|
50
51
|
Restricted read-only role agents (`planner`, `architect`, `critic`) must pass markdown through `GJC_RALPLAN_ARTIFACT` with `--artifact-env GJC_RALPLAN_ARTIFACT`; their restricted bash environment disables artifact file-path ingestion.
|
|
51
52
|
|
|
52
|
-
RECEIPT-ONLY guideline: role agents (`planner`, `architect`, and `critic`) persist durable outputs via `gjc ralplan --write` and return ONLY the receipt fields (`run_id`, `path`, `sha256`) plus verdict/status routing fields; include `stage` and `stage_n` when available, and never return the full persisted body.
|
|
53
|
+
RECEIPT-ONLY guideline: role agents (`planner`, `architect`, and `critic`) persist durable outputs via `gjc ralplan --write` and return ONLY the receipt fields (`session_id`, `run_id`, `path`, `sha256`) plus verdict/status routing fields; include `stage` and `stage_n` when available, and never return the full persisted body.
|
|
54
|
+
|
|
55
|
+
The ralplan seed/write receipt's `session_id` is the immutable workflow owner session and `run_id` is the run identity. Include both in every Planner/Architect/Critic assignment and every parent-side revision/post-interview/ADR/final write. A role subagent's own session id is transcript/resume identity only and MUST NOT own ralplan state or artifacts.
|
|
53
56
|
|
|
54
57
|
This skill runs GJC planning in consensus mode for the provided arguments.
|
|
55
58
|
|
|
@@ -85,25 +88,25 @@ The consensus workflow:
|
|
|
85
88
|
- Persist each Planner revision with `gjc ralplan --write --stage revision --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json` before re-review, then pass the receipt/path forward instead of duplicating the full revision markdown in the parent conversation.
|
|
86
89
|
d. Re-join Architect and Critic verdicts for the same revised Planner artifact/pass
|
|
87
90
|
e. Repeat this loop until Critic returns `OKAY` **and** Architect is `CLEAR`/`APPROVE` for the same Planner artifact/pass, or 5 iterations are reached
|
|
88
|
-
f. If 5 iterations are reached without Critic `OKAY` plus Architect `CLEAR`/`APPROVE`, **stop opening further planner/revision passes**.
|
|
89
|
-
g. **Runtime budget (#3165):** native `gjc ralplan --write` refuses a new `planner`/`revision` that would open consensus iteration **> max** (default **5**, overridable via `gjc.ralplan.maxIterations` in project/user `.gjc/settings.json`, integer 1..20). Cap uses the same iteration definition as the HUD (`planner`/`revision` openers in `index.jsonl`). Overflow exits **3**, prints operator-visible **`PLANNING-STUCK`** on stdout (and stderr detail; JSON includes `planning_stuck: true`), and still allows `architect`/`critic` within an already-opened pass plus `post-interview`/`adr`/`final` so the best plan can be escalated to `pending approval` without
|
|
91
|
+
f. If 5 iterations are reached without Critic `OKAY` plus Architect `CLEAR`/`APPROVE`, **stop opening further planner/revision passes**. Preserve the best version as a terminal `PLANNING-STUCK` result; do not route it to automatic or explicit execution.
|
|
92
|
+
g. **Runtime budget (#3165):** native `gjc ralplan --write` refuses a new `planner`/`revision` that would open consensus iteration **> max** (default **5**, overridable via `gjc.ralplan.maxIterations` in project/user `.gjc/settings.json`, integer 1..20). Cap uses the same iteration definition as the HUD (`planner`/`revision` openers in `index.jsonl`). Overflow exits **3**, prints operator-visible **`PLANNING-STUCK`** on stdout (and stderr detail; JSON includes `planning_stuck: true`), and still allows `architect`/`critic` within an already-opened pass plus `post-interview`/`adr`/`final` so the best plan can be escalated to `pending approval` without dispatch. A new `--run-id` starts a fresh budget.
|
|
90
93
|
6. **Post-ralplan interview** (intent reconciliation gate): After the review join gate has both Critic `OKAY` and Architect `CLEAR`/`APPROVE` for the same Planner artifact/pass, and before the plan is finalized, reconcile the consensus plan against the user's actual intent. The goal is to make sure ralplan did not silently bake in assumptions that conflict with what the user wants.
|
|
91
94
|
a. **Collect open items** from the run: every assumption the Planner/Architect/Critic resolved by assumption rather than by stated fact, every ambiguity flagged during review, and every decision the loop made without explicit user input. Source these from the persisted `planner`/`architect`/`critic`/`revision` stage artifacts, not from memory.
|
|
92
95
|
b. **Cross-check prior context for conflicts**: glob `.gjc/_session-{sessionid}/specs/deep-interview-*.md` and other prior specs/plans/context relevant by topic. For each, list points where the consensus plan contradicts, weakens, or expands beyond a previously crystallized decision, constraint, or non-goal. Cite the conflicting artifact and line/section.
|
|
93
96
|
c. **Reconcile with the user via the `ask` tool (always, regardless of `--interactive`)**: Never stop idle with plain-text prose after the consensus loop. Every reconciliation question MUST go through the `ask` tool with contextual options plus free-text.
|
|
94
97
|
- If open items exist, confirm the open assumptions and conflicts **one at a time** with the `ask` tool, weakest/highest-impact first, polishing intent. If any confirmation reveals that the plan diverges from user intent, route the consolidated correction back into the re-review loop (step 5b Planner revision) and re-run Architect + Critic before returning here. Cap at the same 5-iteration ceiling.
|
|
95
|
-
- If the plan is crystal clear (no open assumptions or prior-context conflicts),
|
|
98
|
+
- If the plan is crystal clear (no open assumptions or prior-context conflicts), continue to final persistence in step 7; do not choose an approval or handoff path before its final receipt exists.
|
|
96
99
|
- For every confirmed open item, embed the resolved outcome into the final plan under an **## Intent Reconciliation** section so the `pending approval` artifact records each decision; record any item the user explicitly defers as an open confirmation under that same section.
|
|
97
100
|
d. Persist the reconciliation with `gjc ralplan --write --stage post-interview --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`, then return the receipt/path plus a compact status (reconciled-clean / reconciled-with-revision / open-confirmations-pending) instead of pasting the full body.
|
|
98
|
-
7. On reconciliation completion, re-check the review join gate (Critic `OKAY` plus Architect `CLEAR`/`APPROVE` for the same Planner artifact/pass), mark the plan `pending approval` unless
|
|
99
|
-
8. **Final approval gate
|
|
101
|
+
7. On reconciliation completion, re-check the review join gate (Critic `OKAY` plus Architect `CLEAR`/`APPROVE` for the same Planner artifact/pass), mark the plan `pending approval` unless execution is already authorized by the resolved handoff admission, then persist the ADR/final plan via `gjc ralplan --write --stage final --stage_n <N> --artifact-env GJC_RALPLAN_ARTIFACT --json`. Read the successful receipt's `auto_handoff` object; its ledger-backed `effectiveTarget` is runtime-owned and is the only automatic-routing decision; do not directly edit `.gjc/_session-{sessionid}/plans`. Final plan must include ADR (Decision, Drivers, Alternatives considered, Why chosen, Consequences, Follow-ups) and, when present, the **## Intent Reconciliation** section.
|
|
102
|
+
8. **Final admission and approval gate:** Reconciliation must first reach the successful final receipt from step 7. If that receipt has `auto_handoff.degradationReason: "planning_stuck"`, it is terminal: retain the `pending approval` artifact and **never dispatch**, including for an explicitly named execution skill; do not issue an approval `ask`. Otherwise, if its runtime-owned `auto_handoff.effectiveTarget` is `ultragoal` or `team`, that valid non-off receipt is explicit operator admission for same-turn execution through that target; proceed to step 9 without an `ask`. If it is `off`, including ordinary `off` or a runtime degradation such as `team_unavailable:<reason>`, preserve the ordinary approval flow: if the user already explicitly named an execution skill in the current turn or via the structured approval UI (`ultragoal`, `/skill:ultragoal`, `gjc ultragoal`, `team`, `/skill:team`, `gjc team`, or "Approve execution via ultragoal/team"), that is execution approval — skip the re-ask and proceed to step 9 with that skill. Otherwise, present the finalized plan via the `ask` tool (regardless of `--interactive`) with `workflowGate: { stage: "ralplan", kind: "approval" }` on the final question so RPC/headless clients receive a `ralplan`/`approval` workflow gate, not a deep-interview question gate. Use these options:
|
|
100
103
|
- **Refine further** — re-run the consensus loop / request changes, then return here
|
|
101
104
|
- **Approve execution via ultragoal (Recommended)** — goal-tracked autonomous execution
|
|
102
105
|
- **Approve execution via team** — only when tmux-based interactive worker parallelization is required
|
|
103
106
|
- **Stop here** — keep the plan as `pending approval` and make no further changes
|
|
104
107
|
|
|
105
|
-
Always include a free-text option. Do not stop with plain text and no `ask
|
|
106
|
-
9. On approval
|
|
108
|
+
Always include a free-text option for the ordinary `off`/degraded approval flow. Do not stop with plain text and no `ask` in that flow; its terminal action is this `ask`.
|
|
109
|
+
9. On valid automatic admission or explicit approval, invoke the admitted/approved `/skill:ultragoal` target by default; invoke `/skill:team` only when the admitted/approved target is `team`. On **Refine further**, return to the step 5 re-review loop. On **Stop here**, leave the `pending approval` artifact and stop. A `planning_stuck` final receipt never reaches this step. Never implement directly.
|
|
107
110
|
|
|
108
111
|
Before invoking `/skill:team` or `/skill:ultragoal`, mark ralplan ready for handoff so the skill tool's chain guard permits the transition:
|
|
109
112
|
|
|
@@ -118,9 +121,9 @@ The consensus workflow:
|
|
|
118
121
|
## Consensus iteration cap (operator contract)
|
|
119
122
|
|
|
120
123
|
- Default max consensus iterations: **5** (`gjc.ralplan.maxIterations`).
|
|
121
|
-
- On cap: exit code **3**, marker **`PLANNING-STUCK`** (stdout), no silent re-loop, no automatic ultragoal/team
|
|
124
|
+
- On cap: exit code **3**, marker **`PLANNING-STUCK`** (stdout), no silent re-loop, no automatic or explicit ultragoal/team dispatch. Opener budget is `max(index.jsonl openers, on-disk stage-*-{planner,revision}.md count)` so a missing/empty/malformed ledger cannot fail open after prior openers.
|
|
122
125
|
- Headless/CI: treat `PLANNING-STUCK` / exit 3 as terminal planning failure for orchestration/watchdogs.
|
|
123
|
-
- Interactive:
|
|
126
|
+
- Interactive: retain the best existing plan as a terminal planning result; residual critic findings stay as caveats.
|
|
124
127
|
- Override example (project `.gjc/settings.json`):
|
|
125
128
|
|
|
126
129
|
```json
|
|
@@ -201,6 +201,8 @@ sleep 30 && gjc team monitor <team-name>
|
|
|
201
201
|
```
|
|
202
202
|
The mutating monitor path also performs bounded liveness recovery: expired task claims, stale heartbeat claims, and missing recorded worker panes are requeued instead of leaving work permanently `in_progress`.
|
|
203
203
|
|
|
204
|
+
A GJC worker session publishes its own heartbeat while an agent turn or owned background job is active, at a third of the stale window, so a long build or test run is no longer reported stale. A worker that publishes nothing — for example one wedged before it could report — is still recovered on the normal window.
|
|
205
|
+
|
|
204
206
|
### Opt-in stalled-worker continuation
|
|
205
207
|
|
|
206
208
|
`GJC_TEAM_AUTO_CONTINUE_STALLED_WORKERS=1` enables a separate, default-off monitor-only nudge for a stalled live worker. It is considered only when the team is running (not dry-run), the worker heartbeat is stale (using `GJC_TEAM_HEARTBEAT_STALE_MS`, default `120000` ms), and all of these checks pass:
|