@gajae-code/coding-agent 0.7.6 → 0.7.8

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.
Files changed (111) hide show
  1. package/CHANGELOG.md +26 -1
  2. package/README.md +8 -0
  3. package/dist/types/cli/local-provider-smoke.d.ts +59 -0
  4. package/dist/types/cli/notify-cli.d.ts +1 -1
  5. package/dist/types/cli/update-cli.d.ts +24 -1
  6. package/dist/types/commands/local-provider.d.ts +34 -0
  7. package/dist/types/config/model-profiles.d.ts +2 -2
  8. package/dist/types/config/model-registry.d.ts +8 -3
  9. package/dist/types/config/models-config-schema.d.ts +5 -5
  10. package/dist/types/config/settings-schema.d.ts +123 -9
  11. package/dist/types/config/settings.d.ts +7 -0
  12. package/dist/types/coordinator-mcp/server.d.ts +8 -0
  13. package/dist/types/export/html/template.generated.d.ts +1 -1
  14. package/dist/types/gjc-runtime/launch-tmux.d.ts +2 -2
  15. package/dist/types/gjc-runtime/state-renderer.d.ts +5 -0
  16. package/dist/types/gjc-runtime/team-runtime.d.ts +1 -1
  17. package/dist/types/gjc-runtime/tmux-common.d.ts +12 -0
  18. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +37 -1
  19. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +80 -0
  20. package/dist/types/modes/components/custom-model-preset-wizard.d.ts +1 -1
  21. package/dist/types/modes/components/model-selector.d.ts +2 -0
  22. package/dist/types/modes/components/settings-selector.d.ts +4 -1
  23. package/dist/types/modes/components/status-line/types.d.ts +5 -7
  24. package/dist/types/modes/components/status-line.d.ts +4 -3
  25. package/dist/types/modes/controllers/input-controller.d.ts +1 -0
  26. package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
  27. package/dist/types/modes/prompt-action-autocomplete.d.ts +1 -0
  28. package/dist/types/modes/tmux-scroll.d.ts +18 -0
  29. package/dist/types/modes/utils/terminal-bell.d.ts +3 -0
  30. package/dist/types/notifications/telegram-daemon-cli.d.ts +13 -2
  31. package/dist/types/tools/browser/tab-supervisor.d.ts +31 -0
  32. package/dist/types/tools/computer-gc.d.ts +23 -0
  33. package/dist/types/tools/cron.d.ts +36 -61
  34. package/dist/types/tools/index.d.ts +0 -1
  35. package/dist/types/tools/resource-gc.d.ts +54 -0
  36. package/dist/types/web/search/index.d.ts +1 -0
  37. package/dist/types/web/search/providers/utils.d.ts +11 -4
  38. package/package.json +7 -7
  39. package/src/cli/args.ts +0 -1
  40. package/src/cli/fast-help.ts +0 -1
  41. package/src/cli/local-provider-smoke.ts +632 -0
  42. package/src/cli/notify-cli.ts +4 -2
  43. package/src/cli/update-cli.ts +155 -13
  44. package/src/cli/web-search-cli.ts +5 -0
  45. package/src/cli.ts +18 -0
  46. package/src/commands/launch.ts +46 -1
  47. package/src/commands/local-provider.ts +62 -0
  48. package/src/commands/notify.ts +1 -1
  49. package/src/config/model-profiles.ts +3 -4
  50. package/src/config/model-registry.ts +58 -7
  51. package/src/config/models-config-schema.ts +10 -1
  52. package/src/config/settings-schema.ts +127 -10
  53. package/src/config/settings.ts +11 -0
  54. package/src/coordinator-mcp/server.ts +276 -34
  55. package/src/export/html/template.generated.ts +1 -1
  56. package/src/export/html/template.js +0 -12
  57. package/src/gjc-runtime/launch-tmux.ts +34 -8
  58. package/src/gjc-runtime/launch-worktree.ts +16 -2
  59. package/src/gjc-runtime/state-renderer.ts +13 -0
  60. package/src/gjc-runtime/team-runtime.ts +21 -6
  61. package/src/gjc-runtime/tmux-common.ts +16 -0
  62. package/src/gjc-runtime/tmux-sessions.ts +13 -9
  63. package/src/gjc-runtime/ultragoal-guard.ts +167 -0
  64. package/src/gjc-runtime/ultragoal-runtime.ts +221 -1
  65. package/src/goals/tools/goal-tool.ts +14 -1
  66. package/src/internal-urls/docs-index.generated.ts +7 -7
  67. package/src/modes/components/agent-dashboard.ts +2 -1
  68. package/src/modes/components/custom-model-preset-wizard.ts +30 -189
  69. package/src/modes/components/model-selector.ts +182 -5
  70. package/src/modes/components/session-observer-overlay.ts +3 -3
  71. package/src/modes/components/settings-selector.ts +491 -5
  72. package/src/modes/components/status-line/segments.ts +7 -18
  73. package/src/modes/components/status-line/types.ts +6 -2
  74. package/src/modes/components/status-line.ts +69 -35
  75. package/src/modes/controllers/event-controller.ts +98 -4
  76. package/src/modes/controllers/extension-ui-controller.ts +3 -0
  77. package/src/modes/controllers/input-controller.ts +15 -0
  78. package/src/modes/controllers/selector-controller.ts +18 -4
  79. package/src/modes/prompt-action-autocomplete.ts +8 -0
  80. package/src/modes/tmux-scroll.ts +92 -0
  81. package/src/modes/utils/terminal-bell.ts +48 -0
  82. package/src/notifications/telegram-daemon-cli.ts +112 -9
  83. package/src/notifications/telegram-daemon.ts +4 -0
  84. package/src/prompts/system/system-prompt.md +2 -2
  85. package/src/prompts/tools/cron.md +5 -3
  86. package/src/prompts/tools/read.md +1 -1
  87. package/src/sdk.ts +5 -0
  88. package/src/session/agent-session.ts +118 -5
  89. package/src/slash-commands/builtin-registry.ts +106 -13
  90. package/src/tools/browser/tab-supervisor.ts +86 -2
  91. package/src/tools/computer-gc.ts +66 -0
  92. package/src/tools/computer.ts +2 -0
  93. package/src/tools/cron.ts +75 -112
  94. package/src/tools/index.ts +2 -8
  95. package/src/tools/read.ts +25 -55
  96. package/src/tools/renderers.ts +0 -2
  97. package/src/tools/resource-gc.ts +291 -0
  98. package/src/tools/ultragoal-ask-guard.ts +7 -1
  99. package/src/web/search/index.ts +1 -0
  100. package/src/web/search/providers/utils.ts +22 -5
  101. package/vendor/insane-search/MANIFEST.json +3 -1
  102. package/vendor/insane-search/engine/__init__.py +14 -0
  103. package/vendor/insane-search/engine/content_safety.py +151 -0
  104. package/vendor/insane-search/engine/fetch_chain.py +32 -0
  105. package/vendor/insane-search/engine/tests/test_u8.py +216 -0
  106. package/dist/types/tools/inspect-image-renderer.d.ts +0 -26
  107. package/dist/types/tools/inspect-image.d.ts +0 -31
  108. package/src/prompts/tools/inspect-image-system.md +0 -20
  109. package/src/prompts/tools/inspect-image.md +0 -32
  110. package/src/tools/inspect-image-renderer.ts +0 -103
  111. package/src/tools/inspect-image.ts +0 -172
@@ -1,4 +1,5 @@
1
1
  import * as crypto from "node:crypto";
2
+ import * as os from "node:os";
2
3
  import * as path from "node:path";
3
4
  import { inflateSync } from "node:zlib";
4
5
  import { normalizeGoal } from "../goals/state";
@@ -11,7 +12,13 @@ import { gjcRoot, sessionUltragoalDir } from "./session-layout";
11
12
  import { resolveGjcSessionForRead, resolveGjcSessionForWrite, writeSessionActivityMarker } from "./session-resolution";
12
13
  import { renderUltragoalStatusMarkdown } from "./state-renderer";
13
14
  import { reconcileWorkflowSkillState } from "./state-runtime";
14
- import { appendJsonl, persistedStateRevision, writeArtifact, writeGuardedJsonAtomic } from "./state-writer";
15
+ import {
16
+ appendJsonl,
17
+ persistedStateRevision,
18
+ withWorkflowStateLock,
19
+ writeArtifact,
20
+ writeGuardedJsonAtomic,
21
+ } from "./state-writer";
15
22
 
16
23
  export type UltragoalGjcGoalMode = "aggregate" | "per-story";
17
24
  export type UltragoalGoalStatus =
@@ -80,6 +87,44 @@ export interface UltragoalLedgerEvent extends JsonObject {
80
87
  timestamp?: string;
81
88
  }
82
89
 
90
+ export type UltragoalNudgeSurface = "pause" | "drop" | "ask" | "premature_complete";
91
+ export type UltragoalNudgeTargetKind = "story" | "final_aggregate_receipt";
92
+
93
+ export interface UltragoalNudgeLedgerEvent extends UltragoalLedgerEvent {
94
+ event: "nudge";
95
+ goalId: string;
96
+ targetKind: UltragoalNudgeTargetKind;
97
+ surface: UltragoalNudgeSurface;
98
+ attempt: number;
99
+ budget: number;
100
+ reason: string;
101
+ currentGoalObjective?: string;
102
+ }
103
+
104
+ export interface UltragoalNudgeTarget {
105
+ goalId: string;
106
+ targetKind: UltragoalNudgeTargetKind;
107
+ }
108
+
109
+ export type UltragoalNudgeOutcome =
110
+ | {
111
+ nudged: true;
112
+ attempt: number;
113
+ budget: number;
114
+ goalId: string;
115
+ targetKind: UltragoalNudgeTargetKind;
116
+ event: UltragoalNudgeLedgerEvent;
117
+ }
118
+ | {
119
+ nudged: false;
120
+ exhausted: true;
121
+ count: number;
122
+ budget: number;
123
+ goalId: string;
124
+ targetKind: UltragoalNudgeTargetKind;
125
+ }
126
+ | { nudged: false; inactive: true; reason: string };
127
+
83
128
  export interface UltragoalPaths {
84
129
  dir: string;
85
130
  briefPath: string;
@@ -95,6 +140,11 @@ export interface UltragoalStatusSummary {
95
140
  currentGoal?: UltragoalGoal;
96
141
  counts: Record<UltragoalGoalStatus, number>;
97
142
  goals: UltragoalGoal[];
143
+ nudgeBudget?: number;
144
+ nudgeCount?: number;
145
+ nudgeRemaining?: number;
146
+ nudgeGoalId?: string;
147
+ nudgeTargetKind?: UltragoalNudgeTargetKind;
98
148
  }
99
149
 
100
150
  export interface UltragoalCommandResult {
@@ -220,6 +270,156 @@ export async function readUltragoalLedger(cwd: string, sessionId?: string | null
220
270
  }
221
271
  }
222
272
 
273
+ export const DEFAULT_ULTRAGOAL_NUDGE_BUDGET = 10;
274
+
275
+ /** Pure: count ledger `nudge` rows for an exact goalId. */
276
+ export function countUltragoalNudges(ledger: readonly UltragoalLedgerEvent[], goalId: string): number {
277
+ return ledger.filter(event => event.event === "nudge" && event.goalId === goalId).length;
278
+ }
279
+
280
+ function parseNudgeBudgetValue(value: unknown): number | null {
281
+ return typeof value === "number" && Number.isFinite(value) && Number.isInteger(value) && value >= 0 ? value : null;
282
+ }
283
+
284
+ async function readSettingsNudgeBudget(settingsPath: string): Promise<number | null> {
285
+ try {
286
+ const raw = await Bun.file(settingsPath).text();
287
+ const parsed = JSON.parse(raw) as Record<string, unknown>;
288
+ // Support both the flat dotted key and a nested gjc.ultragoal.nudgeBudget shape.
289
+ const flat = parseNudgeBudgetValue(parsed["gjc.ultragoal.nudgeBudget"]);
290
+ if (flat !== null) return flat;
291
+ const gjc = parsed.gjc;
292
+ if (gjc && typeof gjc === "object") {
293
+ const ultragoal = (gjc as Record<string, unknown>).ultragoal;
294
+ if (ultragoal && typeof ultragoal === "object") {
295
+ return parseNudgeBudgetValue((ultragoal as Record<string, unknown>).nudgeBudget);
296
+ }
297
+ }
298
+ return null;
299
+ } catch {
300
+ return null;
301
+ }
302
+ }
303
+
304
+ /**
305
+ * Resolve the per-story nudge budget. Project `./.gjc/settings.json` overrides the
306
+ * user settings (`$GJC_CONFIG_DIR/settings.json` or `~/.gjc/settings.json`), else the
307
+ * default. Mirrors the `gjc.deepInterview.ambiguityThreshold` user+project precedence.
308
+ */
309
+ export async function resolveUltragoalNudgeBudget(cwd: string): Promise<{ budget: number; source: string }> {
310
+ const projectPath = path.join(gjcRoot(cwd), "settings.json");
311
+ const project = await readSettingsNudgeBudget(projectPath);
312
+ if (project !== null) return { budget: project, source: projectPath };
313
+ const userDir = process.env.GJC_CONFIG_DIR?.trim() || path.join(os.homedir(), ".gjc");
314
+ const userPath = path.join(userDir, "settings.json");
315
+ const user = await readSettingsNudgeBudget(userPath);
316
+ if (user !== null) return { budget: user, source: userPath };
317
+ return { budget: DEFAULT_ULTRAGOAL_NUDGE_BUDGET, source: "default" };
318
+ }
319
+
320
+ /**
321
+ * Pure canonical selector shared by guards and status so `nudgeGoalId` can never
322
+ * diverge between what a guard consumes and what status displays. Prefers the active
323
+ * current-goal objective, then active > pending > failed (matching `chooseNextGoal`),
324
+ * then the aggregate final-receipt target when all stories are complete but the
325
+ * aggregate run still needs a final receipt. Returns null for verified-complete or
326
+ * absent/unrelated plans.
327
+ */
328
+ export function selectUltragoalNudgeTarget(
329
+ plan: UltragoalPlan,
330
+ options: { currentGoalObjective?: string; retryFailed?: boolean } = {},
331
+ ): UltragoalNudgeTarget | null {
332
+ const objective = options.currentGoalObjective?.trim();
333
+ if (objective) {
334
+ const matched = plan.goals.find(
335
+ goal => goal.objective.trim() === objective && SCHEDULABLE_STATUSES.has(goal.status),
336
+ );
337
+ if (matched) return { goalId: matched.id, targetKind: "story" };
338
+ }
339
+ const next = chooseNextGoal(plan, options.retryFailed === true);
340
+ if (next) return { goalId: next.id, targetKind: "story" };
341
+ const completion = getUltragoalRunCompletionState(plan, { retryFailed: options.retryFailed });
342
+ if (completion.needsFinalAggregateReceipt) {
343
+ const required = requiredUltragoalGoals(plan);
344
+ const finalGoal = required.at(-1);
345
+ if (finalGoal) return { goalId: finalGoal.id, targetKind: "final_aggregate_receipt" };
346
+ }
347
+ return null;
348
+ }
349
+
350
+ /**
351
+ * Atomic consuming writer. Locks the ledger path, rereads + counts nudge rows for the
352
+ * target story, and appends exactly one `nudge` row inside the same critical section
353
+ * only while budget remains. Reuses the lockless `appendLedger` inside the lock (it
354
+ * does not acquire a conflicting lock), so concurrent guarded attempts cannot both
355
+ * observe `count = budget - 1` and overshoot the budget.
356
+ */
357
+ export async function recordUltragoalNudgeIfBudgetRemaining(input: {
358
+ cwd: string;
359
+ sessionId?: string | null;
360
+ target: UltragoalNudgeTarget;
361
+ surface: UltragoalNudgeSurface;
362
+ budget: number;
363
+ reason: string;
364
+ currentGoalObjective?: string;
365
+ }): Promise<UltragoalNudgeOutcome> {
366
+ const { cwd, sessionId, target, surface, budget, reason } = input;
367
+ if (!Number.isFinite(budget) || budget <= 0) {
368
+ return {
369
+ nudged: false,
370
+ exhausted: true,
371
+ count: 0,
372
+ budget: Math.max(0, budget | 0),
373
+ goalId: target.goalId,
374
+ targetKind: target.targetKind,
375
+ };
376
+ }
377
+ const resolvedSessionId =
378
+ sessionId?.trim() || resolveGjcSessionForWrite(cwd, { envSessionId: process.env.GJC_SESSION_ID }).gjcSessionId;
379
+ const paths = getUltragoalPaths(cwd, resolvedSessionId);
380
+ return withWorkflowStateLock(
381
+ paths.ledgerPath,
382
+ async () => {
383
+ const ledger = await readUltragoalLedger(cwd, resolvedSessionId);
384
+ const count = countUltragoalNudges(ledger, target.goalId);
385
+ if (count >= budget) {
386
+ return {
387
+ nudged: false,
388
+ exhausted: true,
389
+ count,
390
+ budget,
391
+ goalId: target.goalId,
392
+ targetKind: target.targetKind,
393
+ } as const;
394
+ }
395
+ const attempt = count + 1;
396
+ const entry = (await appendLedger(
397
+ cwd,
398
+ {
399
+ event: "nudge",
400
+ goalId: target.goalId,
401
+ targetKind: target.targetKind,
402
+ surface,
403
+ attempt,
404
+ budget,
405
+ reason,
406
+ ...(input.currentGoalObjective ? { currentGoalObjective: input.currentGoalObjective } : {}),
407
+ },
408
+ resolvedSessionId,
409
+ )) as UltragoalNudgeLedgerEvent;
410
+ return {
411
+ nudged: true,
412
+ attempt,
413
+ budget,
414
+ goalId: target.goalId,
415
+ targetKind: target.targetKind,
416
+ event: entry,
417
+ } as const;
418
+ },
419
+ { cwd },
420
+ );
421
+ }
422
+
223
423
  async function writePlan(cwd: string, plan: UltragoalPlan, sessionId?: string | null): Promise<void> {
224
424
  const resolvedSessionId =
225
425
  sessionId?.trim() || resolveGjcSessionForWrite(cwd, { envSessionId: process.env.GJC_SESSION_ID }).gjcSessionId;
@@ -525,6 +725,20 @@ export async function getUltragoalStatus(cwd: string, sessionId?: string | null)
525
725
  else if (counts.active > 0) status = "active";
526
726
  else if (counts.failed > 0) status = "failed";
527
727
  else if (counts.blocked > 0 || counts.review_blocked > 0) status = "blocked";
728
+ const nudgeTarget = selectUltragoalNudgeTarget(plan, { currentGoalObjective: currentGoal?.objective });
729
+ let nudgeFields: Partial<UltragoalStatusSummary> = {};
730
+ if (nudgeTarget) {
731
+ const { budget } = await resolveUltragoalNudgeBudget(cwd);
732
+ const ledger = await readUltragoalLedger(cwd, resolvedSessionId);
733
+ const nudgeCount = countUltragoalNudges(ledger, nudgeTarget.goalId);
734
+ nudgeFields = {
735
+ nudgeBudget: budget,
736
+ nudgeCount,
737
+ nudgeRemaining: Math.max(0, budget - nudgeCount),
738
+ nudgeGoalId: nudgeTarget.goalId,
739
+ nudgeTargetKind: nudgeTarget.targetKind,
740
+ };
741
+ }
528
742
  return {
529
743
  exists: true,
530
744
  status,
@@ -533,6 +747,7 @@ export async function getUltragoalStatus(cwd: string, sessionId?: string | null)
533
747
  currentGoal,
534
748
  counts,
535
749
  goals: plan.goals,
750
+ ...nudgeFields,
536
751
  };
537
752
  }
538
753
  export function buildUltragoalHudSummary(
@@ -3847,6 +4062,11 @@ async function reconcileUltragoalState(cwd: string): Promise<void> {
3847
4062
  goals_path: summary.paths.goalsPath,
3848
4063
  };
3849
4064
  if (summary.gjcObjective) payload.gjc_objective = summary.gjcObjective;
4065
+ if (summary.nudgeBudget !== undefined) payload.nudge_budget = summary.nudgeBudget;
4066
+ if (summary.nudgeCount !== undefined) payload.nudge_count = summary.nudgeCount;
4067
+ if (summary.nudgeRemaining !== undefined) payload.nudge_remaining = summary.nudgeRemaining;
4068
+ if (summary.nudgeGoalId !== undefined) payload.nudge_goal_id = summary.nudgeGoalId;
4069
+ if (summary.nudgeTargetKind !== undefined) payload.nudge_target_kind = summary.nudgeTargetKind;
3850
4070
  const ledgerText = await Bun.file(summary.paths.ledgerPath)
3851
4071
  .text()
3852
4072
  .catch(() => "");
@@ -4,7 +4,11 @@ import { Text } from "@gajae-code/tui";
4
4
  import { formatNumber, prompt } from "@gajae-code/utils";
5
5
  import * as z from "zod/v4";
6
6
  import type { RenderResultOptions } from "../../extensibility/custom-tools/types";
7
- import { assertCanCompleteCurrentGoal, assertUltragoalPauseAllowed } from "../../gjc-runtime/ultragoal-guard";
7
+ import {
8
+ assertCanCompleteCurrentGoal,
9
+ assertUltragoalDropAllowed,
10
+ assertUltragoalPauseAllowed,
11
+ } from "../../gjc-runtime/ultragoal-guard";
8
12
  import type { Theme, ThemeColor } from "../../modes/theme/theme";
9
13
  import goalDescription from "../../prompts/tools/goal.md" with { type: "text" };
10
14
  import { formatDuration } from "../../slash-commands/helpers/format";
@@ -103,6 +107,15 @@ async function executeGoalOperation(session: ToolSession, params: GoalToolInput)
103
107
  return buildGoalToolResponse(paused?.goal ?? null);
104
108
  }
105
109
  if (params.op === "drop") {
110
+ try {
111
+ await assertUltragoalDropAllowed({
112
+ cwd: session.cwd,
113
+ currentGoal: session.getGoalModeState?.()?.goal ?? null,
114
+ sessionId: session.getSessionId?.(),
115
+ });
116
+ } catch (error) {
117
+ throw new ToolError(error instanceof Error ? error.message : String(error));
118
+ }
106
119
  const dropped = await runtime.dropGoal();
107
120
  return buildGoalToolResponse(dropped ?? null);
108
121
  }