@gajae-code/coding-agent 0.12.0 → 0.12.2
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 +111 -0
- package/dist/types/async/job-manager.d.ts +17 -3
- package/dist/types/config/settings-schema.d.ts +56 -2
- 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-review-conflicts.d.ts +114 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +23 -3
- 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 +37 -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/irc-sidebar.d.ts +16 -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/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/event-controller.d.ts +1 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +2 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -1
- package/dist/types/modes/interactive-mode.d.ts +9 -0
- package/dist/types/modes/theme/theme.d.ts +3 -1
- package/dist/types/modes/types.d.ts +32 -0
- 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 +5 -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 +12 -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 +40 -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 +30 -4
- 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 +7 -1
- package/dist/types/task/output-manager.d.ts +1 -0
- package/dist/types/task/provider-retry-status.d.ts +8 -0
- package/dist/types/task/receipt.d.ts +9 -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 +6 -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/dogfood-ralplan-review-conflicts.ts +272 -0
- package/scripts/generate-sdk-operation-inventory.ts +5 -0
- package/scripts/resident-memory-bench.ts +1341 -0
- package/scripts/run-acp-conformance.ts +290 -0
- package/src/async/job-manager.ts +99 -39
- 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 +53 -2
- package/src/coordinator-mcp/question-gate-codec.ts +1 -1
- package/src/coordinator-mcp/server.ts +5 -12
- package/src/debug/index.ts +42 -17
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +1 -0
- package/src/defaults/gjc/skills/ralplan/SKILL.md +25 -20
- package/src/defaults/gjc/skills/team/SKILL.md +2 -0
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +39 -26
- 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/launch-worktree.ts +103 -1
- package/src/gjc-runtime/ledger-event-renderer.ts +1 -0
- package/src/gjc-runtime/managed-owner-admission.ts +1 -1
- package/src/gjc-runtime/ralplan-review-conflicts.ts +472 -0
- package/src/gjc-runtime/ralplan-runtime.ts +491 -27
- 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-evidence.ts +6 -74
- package/src/gjc-runtime/ultragoal-runtime.ts +635 -612
- package/src/gjc-runtime/workflow-manifest.generated.json +37 -99
- package/src/gjc-runtime/workflow-manifest.ts +21 -70
- package/src/internal-urls/docs-index.generated.ts +16 -16
- package/src/modes/DESIGN.md +104 -0
- package/src/modes/acp/acp-agent.ts +298 -186
- package/src/modes/components/gjc-bundle-settings.ts +508 -0
- package/src/modes/components/irc-sidebar.ts +30 -1
- 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 -15
- package/src/modes/components/transcript-viewer-overlay.ts +99 -24
- package/src/modes/controllers/command-controller.ts +42 -34
- package/src/modes/controllers/event-controller.ts +27 -16
- package/src/modes/controllers/extension-ui-controller.ts +193 -34
- package/src/modes/controllers/selector-controller.ts +160 -22
- package/src/modes/interactive-mode.ts +189 -62
- package/src/modes/jobs-observer.ts +1 -1
- package/src/modes/runtime-init.ts +3 -0
- package/src/modes/theme/theme.ts +11 -9
- package/src/modes/types.ts +88 -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/lifecycle.ts +18 -13
- 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 +7 -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 +12 -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 +33 -0
- package/src/sdk/session.ts +89 -9
- package/src/session/agent-session.ts +844 -173
- 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 +200 -111
- package/src/session/internal/managed-session-storage.ts +492 -62
- package/src/session/session-manager.ts +1614 -435
- package/src/session/session-storage.ts +38 -45
- 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 +280 -45
- package/src/task/output-manager.ts +24 -14
- package/src/task/provider-retry-status.ts +16 -0
- package/src/task/receipt.ts +34 -8
- 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 +6 -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
|
@@ -209,7 +209,12 @@ export function buildActionMarkdown(action: {
|
|
|
209
209
|
if (action.kind === "idle") {
|
|
210
210
|
return action.summary ? `🟢 Agent idle\n${action.summary}` : "🟢 Agent idle";
|
|
211
211
|
}
|
|
212
|
-
const
|
|
212
|
+
const question = (action.question ?? "Question")
|
|
213
|
+
.split(/\r\n|\r|\n/)
|
|
214
|
+
.map(line => line.trimEnd())
|
|
215
|
+
.map(line => (line ? `**${line}**` : ""))
|
|
216
|
+
.join(" \n");
|
|
217
|
+
const heading = `❓ ${question}`;
|
|
213
218
|
const options = action.options ?? [];
|
|
214
219
|
const displayOptions = withRecommendedOptionLabel(options, action.recommendedIndex);
|
|
215
220
|
if (options.length === 0) return `${heading}\n\n(reply with text)`;
|
|
@@ -217,19 +222,43 @@ export function buildActionMarkdown(action: {
|
|
|
217
222
|
return `${heading}\n\n${list}`;
|
|
218
223
|
}
|
|
219
224
|
|
|
225
|
+
export type TelegramNotificationSound = "all" | "important" | "none";
|
|
226
|
+
|
|
227
|
+
export type TelegramDeliveryLane = "ask" | "idle" | "live" | "finalized";
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Resolve Telegram's optional silent-delivery flag. `finalChunk` silences
|
|
231
|
+
* non-final actionable chunks under the `important` policy; `undefined`
|
|
232
|
+
* intentionally omits the field so Telegram uses its normal audible delivery
|
|
233
|
+
* behavior.
|
|
234
|
+
*/
|
|
235
|
+
export function telegramDisableNotification(
|
|
236
|
+
sound: TelegramNotificationSound | undefined,
|
|
237
|
+
lane: TelegramDeliveryLane,
|
|
238
|
+
finalChunk = true,
|
|
239
|
+
): true | undefined {
|
|
240
|
+
if (sound === "none") return true;
|
|
241
|
+
if (sound !== "important") return undefined;
|
|
242
|
+
return (lane === "ask" || lane === "idle") && finalChunk ? undefined : true;
|
|
243
|
+
}
|
|
244
|
+
|
|
220
245
|
/** Send Telegram HTML text chunks sequentially so long messages preserve order. */
|
|
221
246
|
export async function sendTelegramHtmlChunks(
|
|
222
247
|
send: TelegramSend,
|
|
223
248
|
chatId: string,
|
|
224
249
|
text: string,
|
|
225
250
|
inlineKeyboard?: InlineButton[][],
|
|
251
|
+
sound?: TelegramNotificationSound,
|
|
252
|
+
lane: TelegramDeliveryLane = "ask",
|
|
226
253
|
): Promise<void> {
|
|
227
254
|
const chunks = splitTelegramHtml(text);
|
|
228
255
|
for (let i = 0; i < chunks.length; i++) {
|
|
256
|
+
const disableNotification = telegramDisableNotification(sound, lane, i === chunks.length - 1);
|
|
229
257
|
await send("sendMessage", {
|
|
230
258
|
chat_id: chatId,
|
|
231
259
|
text: chunks[i]!,
|
|
232
260
|
parse_mode: TELEGRAM_PARSE_MODE,
|
|
261
|
+
...(disableNotification === true ? { disable_notification: true } : {}),
|
|
233
262
|
...(i === chunks.length - 1 && inlineKeyboard ? { reply_markup: { inline_keyboard: inlineKeyboard } } : {}),
|
|
234
263
|
});
|
|
235
264
|
}
|
|
@@ -348,6 +377,7 @@ export interface TelegramReferenceOptions {
|
|
|
348
377
|
endpointFile: string;
|
|
349
378
|
apiBase?: string;
|
|
350
379
|
fetchImpl?: typeof fetch;
|
|
380
|
+
sound?: TelegramNotificationSound;
|
|
351
381
|
}
|
|
352
382
|
|
|
353
383
|
/**
|
|
@@ -395,7 +425,14 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
|
|
|
395
425
|
controls: msg.controls,
|
|
396
426
|
summary: msg.summary,
|
|
397
427
|
});
|
|
398
|
-
await sendTelegramHtmlChunks(
|
|
428
|
+
await sendTelegramHtmlChunks(
|
|
429
|
+
send,
|
|
430
|
+
opts.chatId,
|
|
431
|
+
rendered.text,
|
|
432
|
+
rendered.inline_keyboard,
|
|
433
|
+
opts.sound,
|
|
434
|
+
msg.kind ?? "ask",
|
|
435
|
+
);
|
|
399
436
|
} else if (msg.type === "action_unavailable") {
|
|
400
437
|
const requiredCapabilities = Array.isArray(msg.requiredCapabilities)
|
|
401
438
|
? msg.requiredCapabilities
|
|
@@ -416,7 +453,7 @@ export async function runTelegramReferenceClient(opts: TelegramReferenceOptions)
|
|
|
416
453
|
// session's forum topic; this reference shows the minimal handling.
|
|
417
454
|
const threaded = renderThreadedFrame(msg as never);
|
|
418
455
|
if (threaded?.text) {
|
|
419
|
-
await sendTelegramHtmlChunks(send, opts.chatId, threaded.text);
|
|
456
|
+
await sendTelegramHtmlChunks(send, opts.chatId, threaded.text, undefined, opts.sound, threaded.lane);
|
|
420
457
|
}
|
|
421
458
|
}
|
|
422
459
|
};
|
|
@@ -18,13 +18,13 @@ export interface ThreadedSend {
|
|
|
18
18
|
method: "sendMessage" | "sendPhoto" | "sendDocument";
|
|
19
19
|
/** Rate-limit lane for prioritisation/fairness. */
|
|
20
20
|
lane: RateLimitLane;
|
|
21
|
-
/** Message text (sendMessage) or
|
|
21
|
+
/** Message text (sendMessage) or media caption (sendPhoto/sendDocument). */
|
|
22
22
|
text?: string;
|
|
23
23
|
/** Base64 image bytes for sendPhoto. */
|
|
24
24
|
photoBase64?: string;
|
|
25
25
|
/** Base64 file bytes for sendDocument. */
|
|
26
26
|
documentBase64?: string;
|
|
27
|
-
/**
|
|
27
|
+
/** Uploaded media MIME type. */
|
|
28
28
|
mime?: string;
|
|
29
29
|
/** Suggested document filename. */
|
|
30
30
|
fileName?: string;
|
|
@@ -100,6 +100,40 @@ function str(v: unknown): string | undefined {
|
|
|
100
100
|
return typeof v === "string" && v.length > 0 ? v : undefined;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
|
+
function baseMimeType(mime: string): string {
|
|
104
|
+
return mime.split(";", 1)[0]!.trim().toLowerCase();
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function supportsTelegramPhotoUpload(mime: string | undefined): boolean {
|
|
108
|
+
if (mime === undefined) return true;
|
|
109
|
+
const normalized = baseMimeType(mime);
|
|
110
|
+
return normalized === "image/jpeg" || normalized === "image/png";
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function imageDocumentFileName(mime: string | undefined): string {
|
|
114
|
+
switch (mime === undefined ? "" : baseMimeType(mime)) {
|
|
115
|
+
case "image/webp":
|
|
116
|
+
return "image.webp";
|
|
117
|
+
case "image/gif":
|
|
118
|
+
return "image.gif";
|
|
119
|
+
case "image/svg+xml":
|
|
120
|
+
return "image.svg";
|
|
121
|
+
case "image/avif":
|
|
122
|
+
return "image.avif";
|
|
123
|
+
case "image/bmp":
|
|
124
|
+
case "image/x-ms-bmp":
|
|
125
|
+
return "image.bmp";
|
|
126
|
+
case "image/tiff":
|
|
127
|
+
return "image.tiff";
|
|
128
|
+
case "image/heic":
|
|
129
|
+
return "image.heic";
|
|
130
|
+
case "image/heif":
|
|
131
|
+
return "image.heif";
|
|
132
|
+
default:
|
|
133
|
+
return "image";
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
103
137
|
function isDotOnlyText(value: string): boolean {
|
|
104
138
|
return /^[.\s]+$/.test(value);
|
|
105
139
|
}
|
|
@@ -244,11 +278,22 @@ export function renderThreadedFrame(frame: ThreadedFrame): ThreadedSend | undefi
|
|
|
244
278
|
const data = str(frame.data);
|
|
245
279
|
if (!data) return undefined;
|
|
246
280
|
const caption = str(frame.caption);
|
|
281
|
+
const mime = str(frame.mime);
|
|
282
|
+
if (!supportsTelegramPhotoUpload(mime)) {
|
|
283
|
+
return {
|
|
284
|
+
method: "sendDocument",
|
|
285
|
+
lane: "finalized",
|
|
286
|
+
documentBase64: data,
|
|
287
|
+
mime,
|
|
288
|
+
fileName: imageDocumentFileName(mime),
|
|
289
|
+
text: finalizeTelegramHtml(caption === undefined ? undefined : escapeHtml(caption)),
|
|
290
|
+
};
|
|
291
|
+
}
|
|
247
292
|
return {
|
|
248
293
|
method: "sendPhoto",
|
|
249
294
|
lane: "finalized",
|
|
250
295
|
photoBase64: data,
|
|
251
|
-
mime
|
|
296
|
+
mime,
|
|
252
297
|
text: finalizeTelegramHtml(caption === undefined ? undefined : escapeHtml(caption)),
|
|
253
298
|
};
|
|
254
299
|
}
|
|
@@ -43,6 +43,8 @@ export interface TopicRecord {
|
|
|
43
43
|
creationLeaseEpoch?: number;
|
|
44
44
|
/** An uncertain delete fences future creation and inbound routing. */
|
|
45
45
|
authorityState?: "active" | "delete_pending";
|
|
46
|
+
/** Provenance for topics created by the user and adopted by GJC. Missing means daemon-created. */
|
|
47
|
+
topicOrigin?: "user_created";
|
|
46
48
|
/** Telegram chat and endpoint authority last proven to use this topic. */
|
|
47
49
|
chatId?: string;
|
|
48
50
|
/** Canonical endpoint tuple (URL + token) that currently holds the lease. */
|
|
@@ -234,6 +236,7 @@ export class TopicRegistry {
|
|
|
234
236
|
...(raw.authorityState === "delete_pending" || fenceSupersedesRecord
|
|
235
237
|
? { authorityState: "delete_pending" as const }
|
|
236
238
|
: {}),
|
|
239
|
+
...(raw.topicOrigin === "user_created" ? { topicOrigin: "user_created" as const } : {}),
|
|
237
240
|
...(isValidBindingString(raw.chatId) ? { chatId: raw.chatId } : {}),
|
|
238
241
|
...(isValidBindingString(raw.endpointKey) ? { endpointKey: raw.endpointKey } : {}),
|
|
239
242
|
...(isValidBindingString(raw.endpointDigest) ? { endpointDigest: raw.endpointDigest } : {}),
|
|
@@ -307,6 +310,21 @@ export class TopicRegistry {
|
|
|
307
310
|
const record = this.topics.get(sessionId);
|
|
308
311
|
return record?.authorityState !== "delete_pending" && this.byTopic.get(record?.topicId ?? "") === sessionId;
|
|
309
312
|
}
|
|
313
|
+
/**
|
|
314
|
+
* Pure read-only availability check for user-topic adoption. Rejects invalid
|
|
315
|
+
* ids and any id already committed, delete-pending/fenced, staged, or
|
|
316
|
+
* ambiguous, without mutating maps, epochs, or persistence. Used inside the
|
|
317
|
+
* {@link getOrCreateTopic} create callback to fail-closed before adopting a
|
|
318
|
+
* user-created topic id.
|
|
319
|
+
*/
|
|
320
|
+
isTopicIdAvailable(topicId: string): boolean {
|
|
321
|
+
if (!isValidTopicId(topicId)) return false;
|
|
322
|
+
if (this.#ambiguousTopicIds.has(topicId)) return false;
|
|
323
|
+
if (this.byTopic.has(topicId)) return false;
|
|
324
|
+
for (const record of this.topics.values()) if (record.topicId === topicId) return false;
|
|
325
|
+
for (const record of this.staged.values()) if (record.topicId === topicId) return false;
|
|
326
|
+
return true;
|
|
327
|
+
}
|
|
310
328
|
|
|
311
329
|
/**
|
|
312
330
|
* Resolve endpoint authority for identity-less replay. An endpoint may bootstrap
|
|
@@ -455,6 +473,7 @@ export class TopicRegistry {
|
|
|
455
473
|
binding?: TopicEndpointBinding,
|
|
456
474
|
commit?: () => Promise<void>,
|
|
457
475
|
transientClaimant?: object,
|
|
476
|
+
topicOrigin?: TopicRecord["topicOrigin"],
|
|
458
477
|
): Promise<TopicRecord> {
|
|
459
478
|
const existing = this.topics.get(sessionId);
|
|
460
479
|
if (existing?.authorityState === "delete_pending") throw new Error("topic authority is deletion-fenced");
|
|
@@ -491,6 +510,7 @@ export class TopicRegistry {
|
|
|
491
510
|
}
|
|
492
511
|
: {}),
|
|
493
512
|
...(revoked ? { authorityState: "delete_pending" as const } : {}),
|
|
513
|
+
...(topicOrigin === "user_created" ? { topicOrigin: "user_created" as const } : {}),
|
|
494
514
|
};
|
|
495
515
|
if (revoked) {
|
|
496
516
|
this.topics.set(sessionId, record);
|
|
@@ -2,6 +2,32 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { chmod, mkdir, mkdtemp, open, readFile, rename, rm, unlink } from "node:fs/promises";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
+
import { postmortem } from "@gajae-code/utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Spill directories this process created under the system temp dir.
|
|
9
|
+
*
|
|
10
|
+
* `close()` removes a store's own directory, but an abnormal exit never reaches
|
|
11
|
+
* it, and these hold real spilled snapshot data rather than a marker. The two
|
|
12
|
+
* sibling temp-artifact caches (`shell-snapshot`, `python-runner-artifact`)
|
|
13
|
+
* already register a postmortem sweep; this does the same.
|
|
14
|
+
*
|
|
15
|
+
* Only `mkdtemp` directories are tracked. A caller-supplied `storageDir` is
|
|
16
|
+
* durable session storage and must never be swept here.
|
|
17
|
+
*/
|
|
18
|
+
const processSpillDirectories = new Set<string>();
|
|
19
|
+
|
|
20
|
+
/** Test seam: spill directories currently tracked for postmortem cleanup. */
|
|
21
|
+
export function trackedSpillDirectoriesForTest(): string[] {
|
|
22
|
+
return [...processSpillDirectories];
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
postmortem.register("sdk-revision-store:spill", async () => {
|
|
26
|
+
for (const dir of [...processSpillDirectories]) {
|
|
27
|
+
processSpillDirectories.delete(dir);
|
|
28
|
+
await rm(dir, { recursive: true, force: true }).catch(() => undefined);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
5
31
|
|
|
6
32
|
export const MAX_REVISIONS_PER_RESOURCE = 8;
|
|
7
33
|
export const MAX_PINNED_REVISIONS = 128;
|
|
@@ -512,7 +538,10 @@ export class RevisionStore {
|
|
|
512
538
|
this.#memoryBytes = 0;
|
|
513
539
|
this.#chunkRefs.clear();
|
|
514
540
|
this.#manifestRefs.clear();
|
|
515
|
-
if (this.#directory)
|
|
541
|
+
if (this.#directory) {
|
|
542
|
+
processSpillDirectories.delete(this.#directory);
|
|
543
|
+
await rm(this.#directory, { recursive: true, force: true });
|
|
544
|
+
}
|
|
516
545
|
})();
|
|
517
546
|
return this.#closePromise;
|
|
518
547
|
}
|
|
@@ -1040,7 +1069,12 @@ export class RevisionStore {
|
|
|
1040
1069
|
}
|
|
1041
1070
|
|
|
1042
1071
|
async #spillDirectory(): Promise<string> {
|
|
1043
|
-
if (!this.#directory)
|
|
1072
|
+
if (!this.#directory) {
|
|
1073
|
+
this.#directory = await mkdtemp(join(tmpdir(), "gjc-sdk-snapshots-"));
|
|
1074
|
+
// Only temp spill directories are swept on abnormal exit; a caller-supplied
|
|
1075
|
+
// storageDir is durable and never enters this set.
|
|
1076
|
+
processSpillDirectories.add(this.#directory);
|
|
1077
|
+
}
|
|
1044
1078
|
await mkdir(join(this.#directory, "objects"), { recursive: true, mode: 0o700 });
|
|
1045
1079
|
await mkdir(join(this.#directory, "manifests"), { recursive: true, mode: 0o700 });
|
|
1046
1080
|
await chmod(this.#directory, 0o700);
|
|
@@ -41,6 +41,8 @@ interface Outstanding {
|
|
|
41
41
|
leaseId: string;
|
|
42
42
|
resolve: (value: unknown) => void;
|
|
43
43
|
reject: (reason: Error) => void;
|
|
44
|
+
signal?: AbortSignal;
|
|
45
|
+
onAbort?: () => void;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export interface ReverseLeaseOptions {
|
|
@@ -188,7 +190,7 @@ export class ReverseLeaseRuntime {
|
|
|
188
190
|
this.#cancelForConnection(connectionId, "provider_disconnected");
|
|
189
191
|
}
|
|
190
192
|
|
|
191
|
-
request(capability: string, method: string, payload: unknown): Promise<unknown> {
|
|
193
|
+
request(capability: string, method: string, payload: unknown, signal?: AbortSignal): Promise<unknown> {
|
|
192
194
|
if (this.#disposing) throw new Error("reverse runtime is disposing");
|
|
193
195
|
this.#assertPayload(payload);
|
|
194
196
|
const lease = this.#liveLease(capability);
|
|
@@ -198,16 +200,42 @@ export class ReverseLeaseRuntime {
|
|
|
198
200
|
throw new ReverseLeaseError("lease_unavailable");
|
|
199
201
|
throw new ReverseLeaseError("provider_required");
|
|
200
202
|
}
|
|
203
|
+
if (signal?.aborted)
|
|
204
|
+
return Promise.reject(Object.assign(new Error("request_cancelled"), { name: "request_cancelled" }));
|
|
201
205
|
if (this.#outstanding.size >= MAX_REVERSE_OUTSTANDING) throw new ReverseLeaseError("too_many_outstanding");
|
|
202
206
|
const id = randomUUID();
|
|
203
207
|
return new Promise((resolve, reject) => {
|
|
204
|
-
|
|
208
|
+
const outstanding: Outstanding = {
|
|
205
209
|
connectionId: lease.connectionId,
|
|
206
210
|
capability,
|
|
207
211
|
leaseId: lease.leaseId,
|
|
208
212
|
resolve,
|
|
209
213
|
reject,
|
|
210
|
-
|
|
214
|
+
...(signal ? { signal } : {}),
|
|
215
|
+
};
|
|
216
|
+
this.#outstanding.set(id, outstanding);
|
|
217
|
+
if (signal) {
|
|
218
|
+
outstanding.onAbort = () => {
|
|
219
|
+
if (this.#takeOutstanding(id) !== outstanding) return;
|
|
220
|
+
try {
|
|
221
|
+
const cancellation = this.#sendFrame(lease.connectionId, {
|
|
222
|
+
type: "reverse_cancel",
|
|
223
|
+
id,
|
|
224
|
+
connectionId: lease.connectionId,
|
|
225
|
+
leaseId: lease.leaseId,
|
|
226
|
+
});
|
|
227
|
+
void Promise.resolve(cancellation).catch(() => {});
|
|
228
|
+
} catch {
|
|
229
|
+
// Cancellation is best effort; the caller is already settled locally.
|
|
230
|
+
}
|
|
231
|
+
reject(Object.assign(new Error("request_cancelled"), { name: "request_cancelled" }));
|
|
232
|
+
};
|
|
233
|
+
signal.addEventListener("abort", outstanding.onAbort, { once: true });
|
|
234
|
+
if (signal.aborted) {
|
|
235
|
+
outstanding.onAbort();
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
211
239
|
let delivery: void | Promise<void>;
|
|
212
240
|
try {
|
|
213
241
|
delivery = this.#sendFrame(lease.connectionId, {
|
|
@@ -219,12 +247,12 @@ export class ReverseLeaseRuntime {
|
|
|
219
247
|
payload: { method, payload },
|
|
220
248
|
});
|
|
221
249
|
} catch (error) {
|
|
222
|
-
this.#
|
|
250
|
+
this.#takeOutstanding(id);
|
|
223
251
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
224
252
|
return;
|
|
225
253
|
}
|
|
226
254
|
Promise.resolve(delivery).catch(error => {
|
|
227
|
-
this.#
|
|
255
|
+
if (this.#takeOutstanding(id) !== outstanding) return;
|
|
228
256
|
reject(error instanceof Error ? error : new Error(String(error)));
|
|
229
257
|
});
|
|
230
258
|
});
|
|
@@ -242,7 +270,7 @@ export class ReverseLeaseRuntime {
|
|
|
242
270
|
if (!request) throw new ReverseLeaseError("unknown_request");
|
|
243
271
|
if (request.connectionId !== connectionId || request.leaseId !== leaseId)
|
|
244
272
|
throw new ReverseLeaseError("not_lease_owner");
|
|
245
|
-
this.#
|
|
273
|
+
this.#takeOutstanding(id);
|
|
246
274
|
if (error) {
|
|
247
275
|
const rejection = new Error(error.message);
|
|
248
276
|
rejection.name = error.code;
|
|
@@ -270,6 +298,8 @@ export class ReverseLeaseRuntime {
|
|
|
270
298
|
this.#installedCapabilities.clear();
|
|
271
299
|
this.#leases.clear();
|
|
272
300
|
this.#idempotency.clear();
|
|
301
|
+
for (const request of outstanding.map(([, request]) => request))
|
|
302
|
+
if (request.signal && request.onAbort) request.signal.removeEventListener("abort", request.onAbort);
|
|
273
303
|
for (const [id, request] of outstanding) {
|
|
274
304
|
request.reject(new Error("request_cancelled"));
|
|
275
305
|
try {
|
|
@@ -304,11 +334,18 @@ export class ReverseLeaseRuntime {
|
|
|
304
334
|
#cancelForConnection(connectionId: string, reason: "provider_disconnected" | "lease_released"): void {
|
|
305
335
|
for (const [id, request] of this.#outstanding)
|
|
306
336
|
if (request.connectionId === connectionId) {
|
|
307
|
-
this.#
|
|
337
|
+
this.#takeOutstanding(id);
|
|
308
338
|
request.reject(new Error("request_cancelled"));
|
|
309
339
|
this.#onCancel?.(id, reason);
|
|
310
340
|
}
|
|
311
341
|
}
|
|
342
|
+
#takeOutstanding(id: string): Outstanding | undefined {
|
|
343
|
+
const request = this.#outstanding.get(id);
|
|
344
|
+
if (!request) return undefined;
|
|
345
|
+
this.#outstanding.delete(id);
|
|
346
|
+
if (request.signal && request.onAbort) request.signal.removeEventListener("abort", request.onAbort);
|
|
347
|
+
return request;
|
|
348
|
+
}
|
|
312
349
|
#installDefinitionsFor(capability: string, definitions: unknown): void {
|
|
313
350
|
this.#installDefinitions?.(capability, definitions);
|
|
314
351
|
this.#installedCapabilities.add(capability);
|
package/src/sdk/prompt-status.ts
CHANGED
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
* prior outcome unknown — callers MUST NOT reuse a clientRef as a retry
|
|
18
18
|
* mechanism and MUST treat `unknown` as uncertainty, not proof of
|
|
19
19
|
* non-execution.
|
|
20
|
-
* -
|
|
21
|
-
*
|
|
20
|
+
* - Prompt records survive process restart: an active prompt is finalized from its
|
|
21
|
+
* durable pending outcome, or as `prompt_failed` when it has none. Only skill
|
|
22
|
+
* records settle as `process_restart`, and a lookup reports `unknown` only after
|
|
23
|
+
* retained-record TTL/capacity eviction.
|
|
22
24
|
* - Q26 tracks prompts accepted through the SDK control surface (which always
|
|
23
25
|
* carries a requesting connection); submissions without a delivery owner are
|
|
24
26
|
* outside the reconciliation contract and hold no reservation.
|
|
@@ -38,8 +40,17 @@ export interface TurnPromptInput {
|
|
|
38
40
|
clientRef?: string;
|
|
39
41
|
}
|
|
40
42
|
|
|
41
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Terminal outcome is preserved exactly; active records never age into terminal. A
|
|
45
|
+
* prompt that is active at process restart is finalized from its durable pending
|
|
46
|
+
* outcome (or `prompt_failed` when it has none), so it never reports as unknown.
|
|
47
|
+
*/
|
|
42
48
|
export type PromptReconciliationStatus = "accepted" | "in_flight" | "terminal_ok" | "failed";
|
|
49
|
+
export type SdkPromptStopReason = "end_turn" | "max_tokens" | "max_turn_requests" | "refusal" | "cancelled";
|
|
50
|
+
export type SdkPromptFailureCode = "prompt_failed" | "prompt_deadline_exceeded";
|
|
51
|
+
export type SdkPromptTerminalOutcome =
|
|
52
|
+
| { kind: "stopped"; reason: SdkPromptStopReason; provenance: "agent" | "client_cancel" }
|
|
53
|
+
| { kind: "failed"; code: SdkPromptFailureCode; message: string; provenance: "agent_failed" | "deadline" };
|
|
43
54
|
|
|
44
55
|
/** Exactly one selector per lookup. */
|
|
45
56
|
export type TurnPromptStatusSelector = { clientRef: string } | { commandId: string; turnId: string };
|
|
@@ -67,6 +78,7 @@ export interface TurnPromptReconciliationTerminalOk extends TurnPromptReconcilia
|
|
|
67
78
|
startedAt?: number;
|
|
68
79
|
/** Epoch milliseconds of the terminal transition. */
|
|
69
80
|
terminalAt: number;
|
|
81
|
+
outcome?: SdkPromptTerminalOutcome;
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
export interface TurnPromptReconciliationFailed extends TurnPromptReconciliationIdentity {
|
|
@@ -75,6 +87,7 @@ export interface TurnPromptReconciliationFailed extends TurnPromptReconciliation
|
|
|
75
87
|
terminalAt: number;
|
|
76
88
|
/** Bounded, sanitized failure detail (code safe-token ≤64, message ≤512). */
|
|
77
89
|
error: { code: string; message: string };
|
|
90
|
+
outcome?: SdkPromptTerminalOutcome;
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
export interface TurnPromptReconciliationUnknown {
|
|
@@ -2531,6 +2531,17 @@
|
|
|
2531
2531
|
"testIds": "not_applicable"
|
|
2532
2532
|
}
|
|
2533
2533
|
},
|
|
2534
|
+
{
|
|
2535
|
+
"sourceId": "agent_session:registerToolSessionCleanup",
|
|
2536
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
2537
|
+
"sourceKind": "agent_session",
|
|
2538
|
+
"decision": "exclude",
|
|
2539
|
+
"rationale": "internal tool lifecycle cleanup registration, not a user-facing SDK control seam",
|
|
2540
|
+
"exclusionMetadata": {
|
|
2541
|
+
"adapterMappings": "not_applicable",
|
|
2542
|
+
"testIds": "not_applicable"
|
|
2543
|
+
}
|
|
2544
|
+
},
|
|
2534
2545
|
{
|
|
2535
2546
|
"sourceId": "agent_session:getAsyncJobSnapshot",
|
|
2536
2547
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -3656,6 +3667,17 @@
|
|
|
3656
3667
|
"packages/coding-agent/test/sdk-operation-inventory.test.ts"
|
|
3657
3668
|
]
|
|
3658
3669
|
},
|
|
3670
|
+
{
|
|
3671
|
+
"sourceId": "agent_session:abortPromptAndWait",
|
|
3672
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
3673
|
+
"sourceKind": "agent_session",
|
|
3674
|
+
"decision": "exclude",
|
|
3675
|
+
"rationale": "internal SDK prompt-terminalization resource fence over a host-captured run handle, not an independent public SDK control seam",
|
|
3676
|
+
"exclusionMetadata": {
|
|
3677
|
+
"adapterMappings": "not_applicable",
|
|
3678
|
+
"testIds": "not_applicable"
|
|
3679
|
+
}
|
|
3680
|
+
},
|
|
3659
3681
|
{
|
|
3660
3682
|
"sourceId": "agent_session:cancelAndSubmit",
|
|
3661
3683
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
@@ -3852,6 +3874,17 @@
|
|
|
3852
3874
|
"testIds": "not_applicable"
|
|
3853
3875
|
}
|
|
3854
3876
|
},
|
|
3877
|
+
{
|
|
3878
|
+
"sourceId": "agent_session:resolveConfiguredDefaultModel",
|
|
3879
|
+
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|
|
3880
|
+
"sourceKind": "agent_session",
|
|
3881
|
+
"decision": "exclude",
|
|
3882
|
+
"rationale": "internal accessor/plumbing, not a user-facing control seam",
|
|
3883
|
+
"exclusionMetadata": {
|
|
3884
|
+
"adapterMappings": "not_applicable",
|
|
3885
|
+
"testIds": "not_applicable"
|
|
3886
|
+
}
|
|
3887
|
+
},
|
|
3855
3888
|
{
|
|
3856
3889
|
"sourceId": "agent_session:recordResumeDefaultModel",
|
|
3857
3890
|
"sourceFile": "packages/coding-agent/src/session/agent-session.ts",
|