@gajae-code/coding-agent 0.13.1 → 0.13.3

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 (45) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/types/config/model-registry.d.ts +1 -0
  3. package/dist/types/config/settings-schema.d.ts +3 -3
  4. package/dist/types/main.d.ts +1 -0
  5. package/dist/types/modes/components/dynamic-theme-text.d.ts +9 -0
  6. package/dist/types/modes/components/gajae-pet-widget.d.ts +5 -5
  7. package/dist/types/modes/components/pet-capability.d.ts +5 -5
  8. package/dist/types/modes/components/settings-selector.d.ts +5 -0
  9. package/dist/types/modes/theme/defaults/index.d.ts +118 -0
  10. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +3 -1
  11. package/dist/types/session/internal/managed-session-storage.d.ts +13 -0
  12. package/dist/types/session/session-manager.d.ts +7 -0
  13. package/dist/types/tools/image-gen.d.ts +1 -0
  14. package/package.json +8 -8
  15. package/src/config/model-profile-activation.ts +3 -1
  16. package/src/config/model-profiles.ts +59 -2
  17. package/src/config/model-registry.ts +16 -1
  18. package/src/config/settings-schema.ts +1 -1
  19. package/src/coordinator-mcp/server.ts +3 -0
  20. package/src/defaults/gjc/extensions/grok-cli-vendor/src/models/catalog.ts +31 -6
  21. package/src/internal-urls/docs-index.generated.ts +7 -6
  22. package/src/main.ts +4 -0
  23. package/src/modes/components/chrome.ts +4 -3
  24. package/src/modes/components/dynamic-theme-text.ts +22 -0
  25. package/src/modes/components/gajae-pet-widget.ts +130 -42
  26. package/src/modes/components/model-selector.ts +1 -6
  27. package/src/modes/components/pet-capability.ts +10 -5
  28. package/src/modes/components/settings-selector.ts +20 -5
  29. package/src/modes/components/status-line/segments.ts +3 -3
  30. package/src/modes/controllers/selector-controller.ts +61 -5
  31. package/src/modes/interactive-mode.ts +4 -0
  32. package/src/modes/theme/defaults/index.ts +2 -0
  33. package/src/modes/theme/defaults/ouroboros.json +118 -0
  34. package/src/sdk/broker/process-incarnation.ts +6 -1
  35. package/src/sdk/bus/telegram-daemon-contract.ts +3 -1
  36. package/src/sdk/bus/telegram-daemon.ts +51 -43
  37. package/src/session/agent-session.ts +39 -0
  38. package/src/session/internal/managed-session-storage.ts +162 -40
  39. package/src/session/session-manager.ts +28 -0
  40. package/src/slash-commands/builtin-registry.ts +62 -8
  41. package/src/task/index.ts +5 -2
  42. package/src/tools/bash.ts +11 -9
  43. package/src/tools/image-gen.ts +322 -126
  44. package/src/tools/todo-contract.ts +55 -3
  45. package/src/tools/todo-write.ts +4 -3
@@ -50,6 +50,8 @@ const TODO_OP_KEYS = new Set(["op", "list", "task", "phase", "items", "text"]);
50
50
  const TODO_INIT_ENTRY_KEYS = new Set(["phase", "items"]);
51
51
  /** `content` is accepted only as a synonym the schema rewrites to `task`. */
52
52
  const TODO_OP_KEYS_WITH_ALIASES = new Set([...TODO_OP_KEYS, "content"]);
53
+ const MAX_REJECTED_KEYS = 8;
54
+ const POSITIONAL_HANDLE_KEYS = new Set(["id", "ids", "index", "taskId", "task_id"]);
53
55
 
54
56
  /**
55
57
  * Exact key corrections. `note` is the one repeatable confusion: it is an `op`
@@ -60,11 +62,27 @@ const TODO_OP_KEYS_WITH_ALIASES = new Set([...TODO_OP_KEYS, "content"]);
60
62
  * an entry here when the replacement is unambiguous — never from fuzzy or
61
63
  * edit-distance matching, because a wrong suggestion costs more turns than no
62
64
  * suggestion.
65
+ *
66
+ * The positional-handle family (`id`, `index`, and spellings of them) is the
67
+ * other repeatable confusion: the tool result renders tasks as a list, so a
68
+ * caller assumes the list is addressable by ordinal. No op has ever taken a
69
+ * handle - a task is addressed by its verbatim content - and the executor
70
+ * already says so, but on the validated model-facing path this validator
71
+ * rejects the key first, so that message never gets its turn. One shared
72
+ * correction keeps both layers telling the same story.
63
73
  */
74
+ const POSITIONAL_HANDLE_CORRECTION =
75
+ 'tasks have no id or index; target a task with "task" set to its exact content, or a whole phase with "phase"';
76
+
64
77
  const TODO_KEY_CORRECTIONS = new Map<string, string>([
65
78
  ["note", 'note is an op, not a key; note operations require both "task" and "text"'],
66
79
  ["newTask", "there is no rename op; re-run init with the corrected list to rename a task"],
67
80
  ["tasks", 'tasks is not a key; append operations take "items"'],
81
+ ["id", POSITIONAL_HANDLE_CORRECTION],
82
+ ["ids", POSITIONAL_HANDLE_CORRECTION],
83
+ ["index", POSITIONAL_HANDLE_CORRECTION],
84
+ ["taskId", POSITIONAL_HANDLE_CORRECTION],
85
+ ["task_id", POSITIONAL_HANDLE_CORRECTION],
68
86
  ]);
69
87
 
70
88
  function unknownKeys(value: object, allowed: Set<string>): string[] {
@@ -72,15 +90,42 @@ function unknownKeys(value: object, allowed: Set<string>): string[] {
72
90
  }
73
91
 
74
92
  function keyRejectionDetail(keys: readonly string[], location?: string): RawArgumentRejectionDetail {
75
- const hints = keys.map(key => TODO_KEY_CORRECTIONS.get(key)).filter((hint): hint is string => hint !== undefined);
76
- const parts = location === undefined ? hints : [location, ...hints];
77
- return parts.length > 0 ? { rejectedKeys: keys, hint: parts.join("; ") } : { rejectedKeys: keys };
93
+ const rejectedKeys = keys.slice(0, MAX_REJECTED_KEYS);
94
+ // Distinct corrections only. A family that shares one correction (every
95
+ // positional handle) would otherwise repeat it once per rejected key and blow
96
+ // past the caller's hint clamp, which truncates the advice mid-sentence.
97
+ const hints = [
98
+ ...new Set(
99
+ keys
100
+ .map(key => TODO_KEY_CORRECTIONS.get(key) ?? TODO_KEY_CORRECTIONS.get(key.split(".").at(-1) ?? ""))
101
+ .filter((hint): hint is string => hint !== undefined),
102
+ ),
103
+ ];
104
+ const omitted = keys.length - rejectedKeys.length;
105
+ const parts = [
106
+ ...(location === undefined ? [] : [location]),
107
+ ...hints,
108
+ ...(omitted > 0 ? [`${omitted} additional rejected keys omitted`] : []),
109
+ ];
110
+ return parts.length > 0 ? { rejectedKeys, hint: parts.join("; ") } : { rejectedKeys };
78
111
  }
79
112
 
80
113
  function isPlainRecord(value: unknown): value is Record<string, unknown> {
81
114
  return typeof value === "object" && value !== null && !Array.isArray(value);
82
115
  }
83
116
 
117
+ function nestedPositionalHandleKeys(entry: Record<string, unknown>): string[] {
118
+ const keys: string[] = [];
119
+ for (const alias of ["task", "content"] as const) {
120
+ const value = entry[alias];
121
+ if (!isPlainRecord(value)) continue;
122
+ for (const key of Object.keys(value)) {
123
+ if (POSITIONAL_HANDLE_KEYS.has(key)) keys.push(`${alias}.${key}`);
124
+ }
125
+ }
126
+ return keys;
127
+ }
128
+
84
129
  /**
85
130
  * Single source of truth for todo_write pre-coercion validation. Every
86
131
  * rejection carries a code so the caller can surface an actionable correction
@@ -119,6 +164,13 @@ export function validateRawTodoArguments(arguments_: Record<string, unknown>): R
119
164
  code: "todo-write-unknown-op-entry-key",
120
165
  detail: keyRejectionDetail(unknownEntryKeys, location),
121
166
  };
167
+ const nestedHandles = nestedPositionalHandleKeys(entry);
168
+ if (nestedHandles.length > 0)
169
+ return {
170
+ outcome: "reject",
171
+ code: "todo-write-unknown-op-entry-key",
172
+ detail: keyRejectionDetail(nestedHandles, location),
173
+ };
122
174
  const target = entry.task ?? entry.content;
123
175
  if ((op === "done" || op === "drop") && !target && !entry.phase) {
124
176
  return { outcome: "reject", code: "todo-write-done-drop-requires-target", detail: { hint: location } };
@@ -539,9 +539,10 @@ export class TodoWriteTool implements AgentTool<typeof todoWriteSchema, TodoWrit
539
539
  entry => (entry.op === "done" || entry.op === "drop") && !entry.task && !entry.phase,
540
540
  );
541
541
  if (missingTarget) {
542
- // Models reach for a positional handle here (`id: "1"`, `index: 2`). Those keys
543
- // are stripped before this point, leaving a targetless op, so the message has to
544
- // name the one thing that does work: the task's content, verbatim.
542
+ // Model-issued calls never arrive here with a positional handle (`id: "1"`,
543
+ // `index: 2`): raw validation rejects the key first and carries the same
544
+ // correction. Bridges that call execute directly (eval's callSessionTool)
545
+ // skip that validation, so this still has to name what actually works.
545
546
  const errors = [
546
547
  `Missing task or phase for ${missingTarget.op} operation. ` +
547
548
  `Pass "task" with the task's exact content, or "phase" with the phase name; ` +