@gajae-code/coding-agent 0.16.0 → 0.16.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 +41 -1
- package/dist/types/async/job-manager.d.ts +32 -1
- package/dist/types/browser-backend.d.ts +12 -0
- package/dist/types/cli/update-cli.d.ts +1 -0
- package/dist/types/commands/sdk.d.ts +2 -0
- package/dist/types/config/billing-path.d.ts +25 -0
- package/dist/types/config/config-schema-version.d.ts +13 -1
- package/dist/types/config/model-registry.d.ts +16 -0
- package/dist/types/config/settings-schema.d.ts +17 -7
- package/dist/types/extensibility/extensions/types.d.ts +8 -3
- package/dist/types/gjc-runtime/launch-worktree.d.ts +25 -1
- package/dist/types/gjc-runtime/session-state-lock.d.ts +22 -0
- package/dist/types/main.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +21 -21
- package/dist/types/modes/components/model-selector.d.ts +4 -0
- package/dist/types/modes/components/session-selector.d.ts +1 -1
- package/dist/types/modes/components/sessions-dashboard.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +16 -2
- package/dist/types/modes/theme/theme.d.ts +2 -1
- package/dist/types/modes/types.d.ts +2 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -1
- package/dist/types/sdk/broker/ensure.d.ts +3 -0
- package/dist/types/sdk/broker/lifecycle-ledger.d.ts +6 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +14 -0
- package/dist/types/sdk/broker/startup-budget.d.ts +52 -0
- package/dist/types/sdk/broker/state-version.d.ts +17 -0
- package/dist/types/sdk/bus/discord-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/index.d.ts +3 -1
- package/dist/types/sdk/bus/kind-aware-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-conversation.d.ts +1 -1
- package/dist/types/sdk/bus/slack-daemon.d.ts +1 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +1 -1
- package/dist/types/sdk/cli/rows.d.ts +8 -2
- package/dist/types/sdk/cli/session-cli.d.ts +2 -1
- package/dist/types/sdk/host/events.d.ts +1 -0
- package/dist/types/sdk/host/query/cursor.d.ts +4 -2
- package/dist/types/sdk/host/query/handlers.d.ts +1 -0
- package/dist/types/sdk/host/query/revision-store.d.ts +1 -0
- package/dist/types/sdk/host/session-runtime.d.ts +18 -17
- package/dist/types/sdk/lifecycle-session.d.ts +1 -1
- package/dist/types/sdk/prompt-deadline-manager.d.ts +2 -5
- package/dist/types/sdk/receipt-state.d.ts +2 -0
- package/dist/types/sdk/router/session-router.d.ts +4 -0
- package/dist/types/sdk/turn-result.d.ts +6 -0
- package/dist/types/session/agent-session.d.ts +47 -7
- package/dist/types/session/fold-coordinator.d.ts +29 -2
- package/dist/types/session/session-manager.d.ts +14 -2
- package/dist/types/tools/ask.d.ts +1 -0
- package/dist/types/tools/bash.d.ts +12 -0
- package/dist/types/tools/browser/launch.d.ts +10 -0
- package/dist/types/tools/browser/registry.d.ts +8 -0
- package/dist/types/tools/index.d.ts +6 -10
- package/dist/types/tools/job.d.ts +3 -1
- package/dist/types/utils/external-editor.d.ts +7 -0
- package/examples/extensions/README.md +1 -1
- package/examples/extensions/pirate.ts +7 -4
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +7 -1
- package/src/async/job-manager.ts +107 -9
- package/src/browser-backend.ts +29 -0
- package/src/cli/plugin-cli.ts +29 -0
- package/src/cli/update-cli.ts +63 -12
- package/src/commands/sdk.ts +22 -7
- package/src/config/billing-path.ts +42 -0
- package/src/config/config-schema-version.ts +18 -1
- package/src/config/model-preset-registry.ts +177 -18
- package/src/config/model-registry.ts +268 -42
- package/src/config/settings-schema.ts +19 -7
- package/src/config/settings.ts +46 -15
- package/src/coordinator-mcp/projection-scan.ts +6 -6
- package/src/coordinator-mcp/server.ts +21 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -1
- package/src/extensibility/extensions/runner.ts +27 -0
- package/src/extensibility/extensions/types.ts +8 -3
- package/src/extensibility/gjc-plugins/installer.ts +51 -11
- package/src/extensibility/runtime-skill-discovery.ts +110 -3
- package/src/extensibility/skills.ts +22 -12
- package/src/gjc-runtime/launch-worktree.ts +285 -4
- package/src/gjc-runtime/session-state-lock.ts +154 -28
- package/src/gjc-runtime/session-state-sidecar.ts +10 -17
- package/src/gjc-runtime/tmux-owner-isolation.ts +16 -1
- package/src/internal-urls/docs-index.generated.ts +10 -10
- package/src/main.ts +4 -1
- package/src/modes/acp/acp-agent.ts +5 -5
- package/src/modes/components/model-selector.ts +76 -8
- package/src/modes/components/session-selector.ts +23 -15
- package/src/modes/components/sessions-dashboard.ts +8 -5
- package/src/modes/controllers/command-controller.ts +1 -1
- package/src/modes/controllers/event-controller.ts +30 -4
- package/src/modes/controllers/extension-ui-controller.ts +13 -13
- package/src/modes/controllers/input-controller.ts +2 -2
- package/src/modes/controllers/selector-controller.ts +10 -3
- package/src/modes/interactive-mode.ts +36 -12
- package/src/modes/prompt-action-autocomplete.ts +30 -4
- package/src/modes/runtime-init.ts +12 -9
- package/src/modes/theme/theme.ts +5 -0
- package/src/modes/types.ts +2 -0
- package/src/modes/utils/ui-helpers.ts +22 -3
- package/src/prompts/agent-fragments/browser-aside.md +22 -0
- package/src/prompts/tools/ask.md +1 -1
- package/src/prompts/tools/bash.md +1 -0
- package/src/prompts/tools/job.md +1 -1
- package/src/sdk/acp/adapter.ts +5 -0
- package/src/sdk/broker/broker.ts +10 -1
- package/src/sdk/broker/ensure.ts +76 -3
- package/src/sdk/broker/lifecycle-ledger.ts +6 -0
- package/src/sdk/broker/lifecycle.ts +254 -34
- package/src/sdk/broker/spawn-substrate.ts +45 -2
- package/src/sdk/broker/startup-budget.ts +144 -2
- package/src/sdk/broker/state-version.ts +17 -0
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +111 -24
- package/src/sdk/bus/discord-daemon.ts +50 -10
- package/src/sdk/bus/index.ts +33 -3
- package/src/sdk/bus/kind-aware-reconciliation.ts +296 -48
- package/src/sdk/bus/lifecycle-commands.ts +4 -0
- package/src/sdk/bus/notification-service.ts +22 -5
- package/src/sdk/bus/prompt-reconciliation.ts +7 -2
- package/src/sdk/bus/reconciliation-store.ts +55 -31
- package/src/sdk/bus/slack-conversation.ts +1 -1
- package/src/sdk/bus/slack-daemon.ts +72 -38
- package/src/sdk/bus/telegram-daemon-contract.ts +1 -1
- package/src/sdk/cli/rows.ts +30 -7
- package/src/sdk/cli/session-cli.ts +33 -21
- package/src/sdk/host/control/dispatch.ts +2 -2
- package/src/sdk/host/events.ts +1 -0
- package/src/sdk/host/host.ts +2 -0
- package/src/sdk/host/query/cursor.ts +52 -7
- package/src/sdk/host/query/handlers.ts +33 -12
- package/src/sdk/host/query/revision-store.ts +12 -0
- package/src/sdk/host/session-runtime.test.ts +463 -8
- package/src/sdk/host/session-runtime.ts +383 -54
- package/src/sdk/host/surface-policy.ts +3 -3
- package/src/sdk/lifecycle-session.ts +7 -2
- package/src/sdk/prompt-deadline-manager.ts +2 -1
- package/src/sdk/protocol/operation-inventory.generated.json +54 -3
- package/src/sdk/protocol/operation-registry.ts +1 -1
- package/src/sdk/receipt-state.ts +10 -0
- package/src/sdk/router/session-router.ts +152 -59
- package/src/sdk/session.ts +60 -41
- package/src/sdk/turn-result.ts +22 -0
- package/src/session/agent-session.ts +1033 -260
- package/src/session/fold-coordinator.ts +40 -3
- package/src/session/session-manager.ts +517 -77
- package/src/slash-commands/builtin-registry.ts +41 -0
- package/src/task/executor.ts +9 -4
- package/src/tools/ask-contract.test.ts +64 -0
- package/src/tools/ask-contract.ts +36 -1
- package/src/tools/ask.ts +10 -8
- package/src/tools/bash-interactive.ts +11 -2
- package/src/tools/bash.ts +156 -33
- package/src/tools/browser/launch.ts +37 -0
- package/src/tools/browser/registry.ts +99 -9
- package/src/tools/cron.ts +6 -4
- package/src/tools/descriptors.test.ts +15 -0
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/index.ts +6 -6
- package/src/tools/job.ts +4 -1
- package/src/tools/monitor.ts +12 -9
- package/src/tools/resolve.ts +30 -10
- package/src/tools/tool-catalog.generated.ts +2 -2
- package/src/utils/external-editor.ts +72 -1
|
@@ -291,8 +291,8 @@ function invoke(
|
|
|
291
291
|
return surface.setQueueMode("steering", input.mode);
|
|
292
292
|
case "queue.follow_up_mode.set":
|
|
293
293
|
return surface.setQueueMode("follow_up", input.mode);
|
|
294
|
-
case "queue.
|
|
295
|
-
return surface.setQueueMode("
|
|
294
|
+
case "queue.tool_interrupt_policy.set":
|
|
295
|
+
return surface.setQueueMode("tool_interrupt", input.mode);
|
|
296
296
|
case "compaction.run":
|
|
297
297
|
return surface.runCompaction();
|
|
298
298
|
case "compaction.auto.set":
|
package/src/sdk/host/events.ts
CHANGED
package/src/sdk/host/host.ts
CHANGED
|
@@ -354,6 +354,8 @@ export class SessionSdkHost {
|
|
|
354
354
|
if (this.events.generation !== generation) return "generation_changed";
|
|
355
355
|
if (this.#readyGeneration === generation) return "already";
|
|
356
356
|
}
|
|
357
|
+
if (!this.#started || this.#stopping) return "not_prepared";
|
|
358
|
+
if (this.events.generation !== generation) return "generation_changed";
|
|
357
359
|
this.#publishReadiness();
|
|
358
360
|
return "activated";
|
|
359
361
|
}
|
|
@@ -18,6 +18,7 @@ export interface CursorEnvelope {
|
|
|
18
18
|
issuedAt?: number;
|
|
19
19
|
/** TTL metadata minted by `CursorRegistry.grant`: wall-clock ms when the cursor expires. */
|
|
20
20
|
expiresAt?: number;
|
|
21
|
+
purpose?: "checkpoint";
|
|
21
22
|
position: unknown;
|
|
22
23
|
direction: string;
|
|
23
24
|
pageShape: unknown;
|
|
@@ -68,6 +69,7 @@ export interface CursorQueryContext {
|
|
|
68
69
|
resourceId?: string;
|
|
69
70
|
direction: string;
|
|
70
71
|
pageShape: unknown;
|
|
72
|
+
purpose?: "checkpoint";
|
|
71
73
|
}
|
|
72
74
|
|
|
73
75
|
export class CursorError extends Error {
|
|
@@ -170,26 +172,68 @@ export class CursorRegistry {
|
|
|
170
172
|
cursor: string,
|
|
171
173
|
connectionId: string,
|
|
172
174
|
expected: CursorQueryContext,
|
|
173
|
-
|
|
175
|
+
_resourceKind: string,
|
|
174
176
|
resourceId: string,
|
|
175
177
|
): Promise<{ cursor: string; envelope: CursorEnvelope }> {
|
|
176
178
|
const envelope = verifyCursor(cursor, this.sessionToken);
|
|
177
|
-
if (!envelope)
|
|
178
|
-
|
|
179
|
+
if (!envelope) {
|
|
180
|
+
this.release(cursor);
|
|
181
|
+
throw new CursorError("invalid_cursor", true);
|
|
182
|
+
}
|
|
183
|
+
const source = this.#active.get(cursor);
|
|
184
|
+
if (!source || envelope.expiresAt === undefined) {
|
|
185
|
+
this.release(cursor);
|
|
186
|
+
throw new CursorError("invalid_cursor", true);
|
|
187
|
+
}
|
|
188
|
+
const validatedAt = this.now();
|
|
189
|
+
if (source.expiresAt <= validatedAt || envelope.expiresAt <= validatedAt) {
|
|
190
|
+
this.release(cursor);
|
|
191
|
+
throw new CursorError("cursor_expired");
|
|
192
|
+
}
|
|
179
193
|
if (
|
|
180
194
|
envelope.sessionId !== expected.sessionId ||
|
|
181
195
|
(expected.resource !== undefined && envelope.resource !== expected.resource) ||
|
|
182
196
|
envelope.direction !== expected.direction ||
|
|
183
|
-
canonicalJson(envelope.pageShape) !== canonicalJson(expected.pageShape)
|
|
197
|
+
canonicalJson(envelope.pageShape) !== canonicalJson(expected.pageShape) ||
|
|
198
|
+
envelope.purpose !== expected.purpose ||
|
|
199
|
+
_resourceKind !== envelope.resource ||
|
|
200
|
+
source.resourceId !== resourceId
|
|
184
201
|
)
|
|
185
202
|
throw new CursorError("invalid_input", false, "cursor does not match query");
|
|
203
|
+
// Preserve the already-validated source while removing expired target
|
|
204
|
+
// grants before capacity accounting. The shared timestamp prevents the
|
|
205
|
+
// source from changing classification at a TTL boundary mid-exchange.
|
|
206
|
+
this.sweep(cursor, validatedAt);
|
|
207
|
+
const targetConnectionCount = [...this.#active.values()].filter(
|
|
208
|
+
item => item.connectionId === connectionId,
|
|
209
|
+
).length;
|
|
210
|
+
if (
|
|
211
|
+
this.#active.size > MAX_CURSORS_PER_SESSION ||
|
|
212
|
+
targetConnectionCount - (source.connectionId === connectionId ? 1 : 0) >= MAX_CURSORS_PER_CONNECTION
|
|
213
|
+
)
|
|
214
|
+
throw new CursorError("snapshot_capacity_exceeded", false);
|
|
186
215
|
const exchanged: CursorEnvelope = {
|
|
187
216
|
...envelope,
|
|
188
217
|
nonce: randomUUID(),
|
|
189
218
|
issuedAt: undefined,
|
|
190
219
|
expiresAt: undefined,
|
|
191
220
|
};
|
|
192
|
-
const
|
|
221
|
+
const issuedAt = this.now();
|
|
222
|
+
exchanged.issuedAt = issuedAt;
|
|
223
|
+
exchanged.expiresAt = issuedAt + CURSOR_TTL_MS;
|
|
224
|
+
const exchangedCursor = signCursor(exchanged, this.sessionToken);
|
|
225
|
+
try {
|
|
226
|
+
this.revisions.transferPin(cursor, exchangedCursor);
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (error instanceof RevisionStoreError) throw new CursorError(error.code, false, error.message);
|
|
229
|
+
throw error;
|
|
230
|
+
}
|
|
231
|
+
this.#active.delete(cursor);
|
|
232
|
+
this.#active.set(exchangedCursor, {
|
|
233
|
+
connectionId,
|
|
234
|
+
resourceId,
|
|
235
|
+
expiresAt: exchanged.expiresAt,
|
|
236
|
+
});
|
|
193
237
|
return { cursor: exchangedCursor, envelope: exchanged };
|
|
194
238
|
}
|
|
195
239
|
|
|
@@ -227,8 +271,9 @@ export class CursorRegistry {
|
|
|
227
271
|
close(): void {
|
|
228
272
|
for (const cursor of this.#active.keys()) this.release(cursor);
|
|
229
273
|
}
|
|
230
|
-
sweep(): void {
|
|
231
|
-
for (const [cursor, active] of this.#active)
|
|
274
|
+
sweep(exceptCursor?: string, at = this.now()): void {
|
|
275
|
+
for (const [cursor, active] of this.#active)
|
|
276
|
+
if (cursor !== exceptCursor && active.expiresAt <= at) this.release(cursor);
|
|
232
277
|
this.revisions.sweep();
|
|
233
278
|
}
|
|
234
279
|
get size(): number {
|
|
@@ -108,6 +108,7 @@ export interface SdkCheckpointRecord {
|
|
|
108
108
|
revision: number;
|
|
109
109
|
generation: number;
|
|
110
110
|
seq: number;
|
|
111
|
+
idle: boolean;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
114
|
function isNonNegativeSafeInteger(value: unknown): value is number {
|
|
@@ -119,10 +120,11 @@ export function isCheckpointRecord(value: unknown): value is SdkCheckpointRecord
|
|
|
119
120
|
const record = value as Record<string, unknown>;
|
|
120
121
|
const keys = Object.keys(record);
|
|
121
122
|
return (
|
|
122
|
-
keys.length ===
|
|
123
|
+
keys.length === 4 &&
|
|
123
124
|
isNonNegativeSafeInteger(record.revision) &&
|
|
124
125
|
isNonNegativeSafeInteger(record.generation) &&
|
|
125
|
-
isNonNegativeSafeInteger(record.seq)
|
|
126
|
+
isNonNegativeSafeInteger(record.seq) &&
|
|
127
|
+
typeof record.idle === "boolean"
|
|
126
128
|
);
|
|
127
129
|
}
|
|
128
130
|
const sources: Record<string, { resource: string; method: keyof SessionSurface; mvcc: boolean }> = {
|
|
@@ -296,9 +298,10 @@ export class QueryHandlers {
|
|
|
296
298
|
let snapshot: unknown;
|
|
297
299
|
// The cursor may arrive either as the top-level continuation cursor or,
|
|
298
300
|
// on Q01 only (validated in `dispatch`), as the `checkpointToken` resume
|
|
299
|
-
// seed. Both are
|
|
300
|
-
//
|
|
301
|
-
const
|
|
301
|
+
// seed. Both are consumed through the same CursorRegistry authority, but
|
|
302
|
+
// only the named input token is required to carry checkpoint purpose.
|
|
303
|
+
const checkpointToken = queryId === "Q01" ? request.input?.checkpointToken : undefined;
|
|
304
|
+
const rawCursorToken = request.cursor ?? checkpointToken;
|
|
302
305
|
if (rawCursorToken !== undefined && typeof rawCursorToken !== "string")
|
|
303
306
|
return this.#error(request, "invalid_request", false, "checkpointToken must be a non-empty string.");
|
|
304
307
|
const cursorToken = rawCursorToken;
|
|
@@ -309,6 +312,8 @@ export class QueryHandlers {
|
|
|
309
312
|
direction: "forward",
|
|
310
313
|
pageShape: { targetBytes: TARGET_PAGE_BYTES },
|
|
311
314
|
});
|
|
315
|
+
if (checkpointToken !== undefined && cursor.purpose !== "checkpoint")
|
|
316
|
+
throw new CursorError("invalid_input", false, "checkpointToken does not carry checkpoint purpose");
|
|
312
317
|
selector = assertCursorSelector(cursorSelector(cursor.position), selector);
|
|
313
318
|
resourceId = selector.resourceId ?? "default";
|
|
314
319
|
revision = cursor.revision;
|
|
@@ -467,10 +472,20 @@ export class QueryHandlers {
|
|
|
467
472
|
resource: "transcript",
|
|
468
473
|
direction: "forward",
|
|
469
474
|
pageShape: { targetBytes: TARGET_PAGE_BYTES },
|
|
475
|
+
purpose: "checkpoint",
|
|
470
476
|
},
|
|
471
477
|
"transcript",
|
|
472
478
|
"default",
|
|
473
479
|
);
|
|
480
|
+
if (!isCheckpointRecord(exchanged.envelope.highWatermark)) {
|
|
481
|
+
this.cursors.release(exchanged.cursor);
|
|
482
|
+
return this.#error(
|
|
483
|
+
request,
|
|
484
|
+
"invalid_input",
|
|
485
|
+
false,
|
|
486
|
+
"checkpointToken is not an authoritative checkpoint.",
|
|
487
|
+
);
|
|
488
|
+
}
|
|
474
489
|
return {
|
|
475
490
|
id: request.id,
|
|
476
491
|
ok: true,
|
|
@@ -494,13 +509,18 @@ export class QueryHandlers {
|
|
|
494
509
|
// Atomic synchronous capture (C9): entries and event-ring watermark come
|
|
495
510
|
// from the same host-owned call, so the pinned snapshot revision and the
|
|
496
511
|
// subscribe position can never straddle a concurrent append.
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
const
|
|
500
|
-
const
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
512
|
+
if (typeof this.surface.getCheckpointSnapshot !== "function")
|
|
513
|
+
return this.#error(request, "unavailable", false, "Atomic session checkpoint capture is unavailable.");
|
|
514
|
+
const captured = this.surface.getCheckpointSnapshot();
|
|
515
|
+
const entries = captured.entries;
|
|
516
|
+
const head = captured.watermark;
|
|
517
|
+
if (!isCheckpointRecord(head))
|
|
518
|
+
return this.#error(
|
|
519
|
+
request,
|
|
520
|
+
"unavailable",
|
|
521
|
+
false,
|
|
522
|
+
"Atomic session checkpoint capture returned an invalid watermark.",
|
|
523
|
+
);
|
|
504
524
|
// Mint the snapshot from the exact entries captured with the watermark;
|
|
505
525
|
// entries appended after this point are excluded from replay by
|
|
506
526
|
// construction (append-during-checkpoint), and the returned token is the
|
|
@@ -517,6 +537,7 @@ export class QueryHandlers {
|
|
|
517
537
|
highWatermark: head,
|
|
518
538
|
nonce,
|
|
519
539
|
position: { offset: 0, selector: { queryId: "Q01" } },
|
|
540
|
+
purpose: "checkpoint",
|
|
520
541
|
direction: "forward",
|
|
521
542
|
pageShape: { targetBytes: TARGET_PAGE_BYTES },
|
|
522
543
|
};
|
|
@@ -524,6 +524,18 @@ export class RevisionStore {
|
|
|
524
524
|
await this.#enforceMemory();
|
|
525
525
|
}
|
|
526
526
|
|
|
527
|
+
transferPin(sourceCursorId: string, replacementCursorId: string): void {
|
|
528
|
+
if (sourceCursorId === replacementCursorId) return;
|
|
529
|
+
if (this.#pinIndex.has(replacementCursorId))
|
|
530
|
+
throw new RevisionStoreError("snapshot_capacity_exceeded", "replacement cursor is already pinned");
|
|
531
|
+
const revision = this.#pinIndex.get(sourceCursorId);
|
|
532
|
+
if (!revision) throw new RevisionStoreError("snapshot_capacity_exceeded", "snapshot pin is unavailable");
|
|
533
|
+
revision.pins.delete(sourceCursorId);
|
|
534
|
+
revision.pins.add(replacementCursorId);
|
|
535
|
+
this.#pinIndex.delete(sourceCursorId);
|
|
536
|
+
this.#pinIndex.set(replacementCursorId, revision);
|
|
537
|
+
}
|
|
538
|
+
|
|
527
539
|
unpin(cursorId: string): void {
|
|
528
540
|
const revision = this.#pinIndex.get(cursorId);
|
|
529
541
|
if (!revision) return;
|