@gajae-code/coding-agent 0.11.4 → 0.11.5
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 +23 -0
- package/dist/types/cli/notify-cli.d.ts +5 -1
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/gjc-runtime/deep-interview-state.d.ts +28 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +3 -0
- package/dist/types/gjc-runtime/managed-owner-admission.d.ts +42 -0
- package/dist/types/gjc-runtime/managed-owner-supervisor.d.ts +41 -0
- package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +9 -0
- package/dist/types/gjc-runtime/ultragoal-owner-loss-recovery.d.ts +94 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +1 -0
- package/dist/types/harness-control-plane/owner.d.ts +3 -1
- package/dist/types/internal-urls/local-protocol.d.ts +18 -0
- package/dist/types/main.d.ts +2 -2
- package/dist/types/modes/components/ansi-display-validator.d.ts +2 -0
- package/dist/types/modes/components/notifications-settings-editor.d.ts +4 -0
- package/dist/types/modes/components/session-observer-overlay.d.ts +3 -1
- package/dist/types/modes/components/settings-selector.d.ts +2 -0
- package/dist/types/modes/components/tool-transcript-format.d.ts +58 -0
- package/dist/types/modes/components/transcript-viewer-overlay.d.ts +3 -0
- package/dist/types/modes/controllers/goal-mode-controller.d.ts +8 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +15 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +3 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +16 -0
- package/dist/types/plan-mode/approved-plan.d.ts +1 -0
- package/dist/types/runtime-mcp/manager.d.ts +2 -0
- package/dist/types/runtime-mcp/oauth-discovery.d.ts +12 -1
- package/dist/types/sdk/bus/chat-daemon-cli.d.ts +3 -1
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +39 -4
- package/dist/types/sdk/bus/config-commands.d.ts +8 -0
- package/dist/types/sdk/bus/config.d.ts +22 -0
- package/dist/types/sdk/bus/index.d.ts +162 -2
- package/dist/types/sdk/bus/notification-service.d.ts +71 -8
- package/dist/types/sdk/bus/session-control.d.ts +11 -8
- package/dist/types/sdk/bus/telegram-daemon-cli.d.ts +5 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +23 -4
- package/dist/types/sdk/bus/telegram-daemon.d.ts +214 -20
- package/dist/types/sdk/session.d.ts +16 -1
- package/dist/types/secrets/index.d.ts +4 -2
- package/dist/types/secrets/obfuscator.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +31 -2
- package/dist/types/session/artifacts.d.ts +30 -8
- package/dist/types/session/internal/managed-session-scope.d.ts +7 -0
- package/dist/types/session/internal/managed-session-storage.d.ts +63 -6
- package/dist/types/session/session-manager.d.ts +88 -18
- package/dist/types/session/session-storage.d.ts +27 -0
- package/dist/types/session/streaming-output.d.ts +4 -0
- package/dist/types/task/executor.d.ts +9 -0
- package/dist/types/tool-discovery/tool-index.d.ts +2 -0
- package/dist/types/tools/ask.d.ts +48 -0
- package/dist/types/tools/bash-skill-urls.d.ts +2 -2
- package/dist/types/tools/index.d.ts +4 -0
- package/package.json +9 -9
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/src/cli/notify-cli.ts +40 -18
- package/src/commands/sdk.ts +7 -2
- package/src/config/settings-schema.ts +17 -0
- package/src/daemon/runtime.ts +2 -2
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +70 -49
- package/src/defaults/gjc/skills/ralplan/SKILL.md +0 -6
- package/src/defaults/gjc/skills/team/SKILL.md +7 -9
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +0 -2
- package/src/eval/py/index.ts +2 -2
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/plugins/marketplace/manager.ts +94 -10
- package/src/gjc-runtime/deep-interview-recorder.ts +7 -0
- package/src/gjc-runtime/deep-interview-runtime.ts +10 -0
- package/src/gjc-runtime/deep-interview-state.ts +127 -3
- package/src/gjc-runtime/launch-tmux.ts +61 -6
- package/src/gjc-runtime/managed-owner-admission.ts +352 -0
- package/src/gjc-runtime/managed-owner-supervisor.ts +162 -0
- package/src/gjc-runtime/state-runtime.ts +38 -12
- package/src/gjc-runtime/team-runtime.ts +1 -1
- package/src/gjc-runtime/tmux-owner-isolation.ts +117 -0
- package/src/gjc-runtime/ultragoal-owner-loss-recovery.ts +328 -0
- package/src/gjc-runtime/ultragoal-runtime.ts +14 -0
- package/src/harness-control-plane/owner.ts +59 -3
- package/src/hindsight/client.ts +65 -2
- package/src/internal-urls/agent-protocol.ts +76 -5
- package/src/internal-urls/docs-index.generated.ts +7 -6
- package/src/internal-urls/local-protocol.ts +416 -18
- package/src/lsp/index.ts +4 -88
- package/src/main.ts +37 -13
- package/src/modes/acp/acp-agent.ts +8 -0
- package/src/modes/components/ansi-display-validator.ts +54 -0
- package/src/modes/components/notifications-settings-editor.ts +26 -2
- package/src/modes/components/session-observer-overlay.ts +40 -24
- package/src/modes/components/settings-selector.ts +3 -1
- package/src/modes/components/tool-transcript-format.ts +321 -0
- package/src/modes/components/transcript-viewer-overlay.ts +39 -17
- package/src/modes/controllers/goal-mode-controller.ts +20 -1
- package/src/modes/controllers/plan-mode-controller.ts +2 -0
- package/src/modes/controllers/runtime-mcp-command-controller.ts +3 -0
- package/src/modes/controllers/selector-controller.ts +70 -8
- package/src/modes/interactive-mode.ts +1 -0
- package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -1
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +17 -1
- package/src/modes/shared/agent-wire/workflow-gate-schema.ts +4 -2
- package/src/modes/shared/agent-wire/workflow-gate-types.ts +120 -3
- package/src/plan-mode/approved-plan.ts +3 -1
- package/src/prompts/agents/architect.md +1 -0
- package/src/prompts/agents/critic.md +1 -0
- package/src/prompts/system/custom-system-prompt.md +1 -1
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/lsp.md +3 -3
- package/src/runtime-mcp/manager.ts +16 -1
- package/src/runtime-mcp/oauth-discovery.ts +116 -26
- package/src/sdk/broker/session-index.ts +122 -64
- package/src/sdk/bus/chat-daemon-cli.ts +72 -10
- package/src/sdk/bus/chat-daemon-control.ts +526 -130
- package/src/sdk/bus/chat-daemon-runtime.ts +1 -0
- package/src/sdk/bus/config-commands.ts +22 -0
- package/src/sdk/bus/config.ts +37 -0
- package/src/sdk/bus/index.ts +384 -92
- package/src/sdk/bus/lifecycle-control-runtime.ts +53 -3
- package/src/sdk/bus/notification-service.ts +284 -62
- package/src/sdk/bus/recent-activity.ts +14 -5
- package/src/sdk/bus/session-control.ts +150 -44
- package/src/sdk/bus/telegram-daemon-cli.ts +20 -14
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -3
- package/src/sdk/bus/telegram-daemon-control.ts +294 -76
- package/src/sdk/bus/telegram-daemon.ts +1788 -267
- package/src/sdk/protocol/operation-inventory.generated.json +11 -0
- package/src/sdk/session.ts +155 -27
- package/src/secrets/index.ts +53 -10
- package/src/secrets/obfuscator.ts +21 -18
- package/src/secrets/regex.ts +2 -0
- package/src/session/agent-session.ts +495 -253
- package/src/session/artifacts.ts +188 -23
- package/src/session/context-estimation.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +200 -38
- package/src/session/internal/managed-session-storage.ts +1152 -46
- package/src/session/session-manager.ts +1055 -148
- package/src/session/session-storage.ts +109 -11
- package/src/session/streaming-output.ts +6 -0
- package/src/task/executor.ts +71 -26
- package/src/task/index.ts +63 -20
- package/src/tool-discovery/tool-index.ts +18 -0
- package/src/tools/ask.ts +129 -32
- package/src/tools/bash-skill-urls.ts +14 -3
- package/src/tools/bash.ts +2 -2
- package/src/tools/index.ts +4 -0
- package/src/tools/plan-mode-guard.ts +1 -0
- package/src/tools/sqlite-reader.ts +3 -2
- package/src/tools/subagent.ts +2 -0
package/src/tools/ask.ts
CHANGED
|
@@ -37,11 +37,18 @@ import {
|
|
|
37
37
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
38
38
|
import { appendOrMergeDeepInterviewRound, syncDeepInterviewRecorderHud } from "../gjc-runtime/deep-interview-recorder";
|
|
39
39
|
import { deepInterviewStatePath } from "../gjc-runtime/deep-interview-runtime";
|
|
40
|
+
import {
|
|
41
|
+
assertDeepInterviewInputWithinLimit,
|
|
42
|
+
assertDeepInterviewStructuredResponseWithinLimit,
|
|
43
|
+
deepInterviewCharacterCount,
|
|
44
|
+
MAX_USER_RESPONSE_LENGTH,
|
|
45
|
+
} from "../gjc-runtime/deep-interview-state";
|
|
40
46
|
import {
|
|
41
47
|
type AskGateQuestion,
|
|
42
48
|
gateAnswerToResult,
|
|
43
49
|
questionToGate,
|
|
44
50
|
} from "../modes/shared/agent-wire/deep-interview-gate";
|
|
51
|
+
|
|
45
52
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
46
53
|
import askDescription from "../prompts/tools/ask.md" with { type: "text" };
|
|
47
54
|
import { renderStatusLine } from "../tui";
|
|
@@ -63,15 +70,35 @@ import { assertUltragoalAskAllowed } from "./ultragoal-ask-guard";
|
|
|
63
70
|
// Types
|
|
64
71
|
// =============================================================================
|
|
65
72
|
|
|
73
|
+
function deepInterviewBoundedString(maximum: number) {
|
|
74
|
+
return z.string().superRefine((value, context) => {
|
|
75
|
+
if (deepInterviewCharacterCount(value) > maximum)
|
|
76
|
+
context.addIssue({
|
|
77
|
+
code: "too_big",
|
|
78
|
+
maximum,
|
|
79
|
+
inclusive: true,
|
|
80
|
+
origin: "string",
|
|
81
|
+
message: `Too big: expected string to have <=${maximum} characters`,
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
|
|
66
86
|
const OptionItem = z.object({
|
|
67
87
|
label: z.string().describe("display label"),
|
|
68
88
|
});
|
|
69
89
|
|
|
90
|
+
const DEEP_INTERVIEW_INTENT_ID_PATTERN = /^(artifact|surface|integration|constraint):[a-z0-9][a-z0-9._/-]{0,127}$/;
|
|
91
|
+
|
|
92
|
+
const DeepInterviewReferenceId = z.string().superRefine((value, context) => {
|
|
93
|
+
if (!DEEP_INTERVIEW_INTENT_ID_PATTERN.test(value))
|
|
94
|
+
context.addIssue({ code: "custom", message: "invalid deep-interview intent ID" });
|
|
95
|
+
});
|
|
96
|
+
|
|
70
97
|
const DeepInterviewIntentItem = z
|
|
71
98
|
.object({
|
|
72
|
-
id: z.string().regex(
|
|
99
|
+
id: z.string().regex(DEEP_INTERVIEW_INTENT_ID_PATTERN),
|
|
73
100
|
category: z.enum(["artifact", "surface", "integration", "constraint"]),
|
|
74
|
-
statement:
|
|
101
|
+
statement: deepInterviewBoundedString(1_000).min(1),
|
|
75
102
|
})
|
|
76
103
|
.strict()
|
|
77
104
|
.superRefine((value, context) => {
|
|
@@ -82,7 +109,7 @@ const DeepInterviewIntentItem = z
|
|
|
82
109
|
const DeepInterviewIntentContract = z
|
|
83
110
|
.object({
|
|
84
111
|
items: z.array(DeepInterviewIntentItem).min(1).max(64),
|
|
85
|
-
confirmation_options: z.array(
|
|
112
|
+
confirmation_options: z.array(deepInterviewBoundedString(200).min(1)).min(1).max(5),
|
|
86
113
|
})
|
|
87
114
|
.strict();
|
|
88
115
|
|
|
@@ -93,43 +120,57 @@ const DeepInterviewIntentReview = z
|
|
|
93
120
|
.array(
|
|
94
121
|
z
|
|
95
122
|
.object({
|
|
96
|
-
removed_id:
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
replacement_ids: z
|
|
100
|
-
.array(z.string().regex(/^(artifact|surface|integration|constraint):[a-z0-9][a-z0-9._/-]{0,127}$/))
|
|
101
|
-
.min(1)
|
|
102
|
-
.max(64),
|
|
103
|
-
rationale: z.string().min(1).max(500),
|
|
123
|
+
removed_id: DeepInterviewReferenceId,
|
|
124
|
+
replacement_ids: z.array(DeepInterviewReferenceId).min(1).max(64),
|
|
125
|
+
rationale: deepInterviewBoundedString(500).min(1),
|
|
104
126
|
})
|
|
105
127
|
.strict(),
|
|
106
128
|
)
|
|
107
129
|
.max(64),
|
|
108
|
-
approval_options: z.array(
|
|
130
|
+
approval_options: z.array(deepInterviewBoundedString(200).min(1)).min(1).max(5),
|
|
109
131
|
})
|
|
110
132
|
.strict();
|
|
111
133
|
|
|
112
134
|
/** Optional structured deep-interview round metadata; when present the round is recorded automatically. */
|
|
113
135
|
const DeepInterviewMetadata = z.object({
|
|
114
|
-
round_id:
|
|
136
|
+
round_id: deepInterviewBoundedString(128).describe("stable optional round identity").optional(),
|
|
115
137
|
round: z.number().int().nonnegative().describe("round number"),
|
|
116
|
-
component:
|
|
117
|
-
dimension:
|
|
138
|
+
component: deepInterviewBoundedString(128).min(1).describe("targeted topology component"),
|
|
139
|
+
dimension: deepInterviewBoundedString(128).min(1).describe("targeted clarity dimension"),
|
|
118
140
|
ambiguity: z.number().min(0).max(1).describe("ambiguity at ask time (0..1)"),
|
|
141
|
+
confused_terms: z
|
|
142
|
+
.array(deepInterviewBoundedString(256).min(1))
|
|
143
|
+
.max(32)
|
|
144
|
+
.describe("explicit terms the user does not understand; glossary help only, never inferred")
|
|
145
|
+
.optional(),
|
|
146
|
+
references: z
|
|
147
|
+
.array(
|
|
148
|
+
z
|
|
149
|
+
.object({
|
|
150
|
+
reference_id: deepInterviewBoundedString(256).min(1),
|
|
151
|
+
label: deepInterviewBoundedString(256).min(1),
|
|
152
|
+
origin: deepInterviewBoundedString(256).min(1),
|
|
153
|
+
url: deepInterviewBoundedString(2048).min(1).optional(),
|
|
154
|
+
excerpt: deepInterviewBoundedString(2048).min(1).optional(),
|
|
155
|
+
})
|
|
156
|
+
.strict(),
|
|
157
|
+
)
|
|
158
|
+
.max(32)
|
|
159
|
+
.describe("inert reference context for contrast questions only; url/excerpt are never auto-fetched")
|
|
160
|
+
.optional(),
|
|
119
161
|
});
|
|
120
162
|
|
|
121
|
-
/** Provider-visible branches keep ordinary, contract, and review metadata mutually exclusive. */
|
|
122
163
|
const DeepInterviewMeta = z.union([
|
|
123
164
|
DeepInterviewMetadata.strict(),
|
|
124
165
|
DeepInterviewMetadata.extend({
|
|
125
|
-
round: z.literal(0)
|
|
126
|
-
component: z.literal("review-topology")
|
|
127
|
-
dimension: z.literal("topology")
|
|
128
|
-
intent_contract: DeepInterviewIntentContract
|
|
166
|
+
round: z.literal(0),
|
|
167
|
+
component: z.literal("review-topology"),
|
|
168
|
+
dimension: z.literal("topology"),
|
|
169
|
+
intent_contract: DeepInterviewIntentContract,
|
|
129
170
|
}).strict(),
|
|
130
171
|
DeepInterviewMetadata.extend({
|
|
131
|
-
round: z.number().int().positive()
|
|
132
|
-
intent_review: DeepInterviewIntentReview
|
|
172
|
+
round: z.number().int().positive(),
|
|
173
|
+
intent_review: DeepInterviewIntentReview,
|
|
133
174
|
}).strict(),
|
|
134
175
|
]);
|
|
135
176
|
|
|
@@ -164,6 +205,29 @@ const QuestionItem = z
|
|
|
164
205
|
const labels = new Set(value.options.map(option => option.label));
|
|
165
206
|
const contract = intentContract(value.deepInterview);
|
|
166
207
|
const review = intentReview(value.deepInterview);
|
|
208
|
+
if (contract && review)
|
|
209
|
+
context.addIssue({
|
|
210
|
+
code: "custom",
|
|
211
|
+
message: "intent contract and review are mutually exclusive",
|
|
212
|
+
path: ["deepInterview"],
|
|
213
|
+
});
|
|
214
|
+
if (
|
|
215
|
+
contract &&
|
|
216
|
+
(value.deepInterview?.round !== 0 ||
|
|
217
|
+
value.deepInterview.component !== "review-topology" ||
|
|
218
|
+
value.deepInterview.dimension !== "topology")
|
|
219
|
+
)
|
|
220
|
+
context.addIssue({
|
|
221
|
+
code: "custom",
|
|
222
|
+
message: "intent contract requires round-0 review topology metadata",
|
|
223
|
+
path: ["deepInterview"],
|
|
224
|
+
});
|
|
225
|
+
if (review && (value.deepInterview?.round ?? 0) <= 0)
|
|
226
|
+
context.addIssue({
|
|
227
|
+
code: "custom",
|
|
228
|
+
message: "intent review requires a positive round",
|
|
229
|
+
path: ["deepInterview", "round"],
|
|
230
|
+
});
|
|
167
231
|
if ((contract || review) && value.multi === true)
|
|
168
232
|
context.addIssue({ code: "custom", message: "intent gates must be single-select", path: ["multi"] });
|
|
169
233
|
const confirmationOptions = contract?.confirmation_options ?? [];
|
|
@@ -211,7 +275,11 @@ function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
|
211
275
|
}
|
|
212
276
|
|
|
213
277
|
function isOnlyPlainData(value: unknown): boolean {
|
|
214
|
-
if (Array.isArray(value))
|
|
278
|
+
if (Array.isArray(value))
|
|
279
|
+
return (
|
|
280
|
+
Reflect.ownKeys(value).length === value.length + 1 &&
|
|
281
|
+
value.every((item, index) => Object.hasOwn(value, index) && isOnlyPlainData(item))
|
|
282
|
+
);
|
|
215
283
|
if (typeof value !== "object" || value === null) return true;
|
|
216
284
|
return isPlainRecord(value) && Object.values(value).every(isOnlyPlainData);
|
|
217
285
|
}
|
|
@@ -270,11 +338,27 @@ function normalizeRoundZeroOptionalNulls(arguments_: Record<string, unknown>): R
|
|
|
270
338
|
}
|
|
271
339
|
}
|
|
272
340
|
const normalizedDeepInterview = { ...question.deepInterview };
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
341
|
+
for (const key of ["round_id", "confused_terms", "references"] as const) {
|
|
342
|
+
if (Object.hasOwn(normalizedDeepInterview, key) && normalizedDeepInterview[key] === null) {
|
|
343
|
+
delete normalizedDeepInterview[key];
|
|
344
|
+
changed = true;
|
|
345
|
+
}
|
|
277
346
|
}
|
|
347
|
+
if (Array.isArray(normalizedDeepInterview.references)) {
|
|
348
|
+
const references = normalizedDeepInterview.references.map(reference => {
|
|
349
|
+
if (!isPlainRecord(reference)) return reference;
|
|
350
|
+
const normalizedReference = { ...reference };
|
|
351
|
+
for (const key of ["url", "excerpt"] as const) {
|
|
352
|
+
if (Object.hasOwn(normalizedReference, key) && normalizedReference[key] === null) {
|
|
353
|
+
delete normalizedReference[key];
|
|
354
|
+
changed = true;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return normalizedReference;
|
|
358
|
+
});
|
|
359
|
+
normalizedDeepInterview.references = references;
|
|
360
|
+
}
|
|
361
|
+
if (changed) normalizedQuestion.deepInterview = normalizedDeepInterview;
|
|
278
362
|
return changed ? { ...arguments_, questions: [normalizedQuestion] } : arguments_;
|
|
279
363
|
}
|
|
280
364
|
function recoverRoundZeroIntentContract(arguments_: Record<string, unknown>): RawArgumentValidationResult {
|
|
@@ -332,6 +416,8 @@ function recoverRoundZeroIntentContract(arguments_: Record<string, unknown>): Ra
|
|
|
332
416
|
"component",
|
|
333
417
|
"dimension",
|
|
334
418
|
"ambiguity",
|
|
419
|
+
"confused_terms",
|
|
420
|
+
"references",
|
|
335
421
|
"intent_contract",
|
|
336
422
|
"intent_review",
|
|
337
423
|
];
|
|
@@ -986,7 +1072,9 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
986
1072
|
}
|
|
987
1073
|
|
|
988
1074
|
static createIf(session: ToolSession): AskTool | null {
|
|
989
|
-
return session.hasUI || session.getWorkflowGateEmitter?.()
|
|
1075
|
+
return session.hasUI || session.workflowGateEligible || session.getWorkflowGateEmitter?.()
|
|
1076
|
+
? new AskTool(session)
|
|
1077
|
+
: null;
|
|
990
1078
|
}
|
|
991
1079
|
|
|
992
1080
|
/** Send terminal notification when ask tool is waiting for input */
|
|
@@ -1007,6 +1095,8 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1007
1095
|
customInput: string | undefined,
|
|
1008
1096
|
): Promise<void> {
|
|
1009
1097
|
const meta = q.deepInterview;
|
|
1098
|
+
if (customInput !== undefined && (meta || isDeepInterviewAskQuestion(q.question)))
|
|
1099
|
+
assertDeepInterviewInputWithinLimit(customInput, MAX_USER_RESPONSE_LENGTH, "user_response");
|
|
1010
1100
|
if (!meta) return;
|
|
1011
1101
|
const cwd = this.session.cwd;
|
|
1012
1102
|
const sessionId = this.session.getSessionId?.() ?? undefined;
|
|
@@ -1047,6 +1137,7 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1047
1137
|
activeSkillState: this.session.getActiveSkillState?.(),
|
|
1048
1138
|
sessionId: this.session.getSessionId?.() ?? null,
|
|
1049
1139
|
});
|
|
1140
|
+
assertDeepInterviewStructuredResponseWithinLimit(params);
|
|
1050
1141
|
let activeRemoteReceipt: AskRemoteReceipt | undefined;
|
|
1051
1142
|
let activeRemoteRequest: AskAnswerRequest | undefined;
|
|
1052
1143
|
let remoteGeneration = 0;
|
|
@@ -1391,6 +1482,8 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1391
1482
|
return displayIndex >= 0 ? (rawOptionLabels[displayIndex] ?? selected) : selected;
|
|
1392
1483
|
})
|
|
1393
1484
|
: displaySelectedOptions;
|
|
1485
|
+
if ((isDeepInterviewQuestion || isDeepInterviewAskQuestion(q.question)) && customInput !== undefined)
|
|
1486
|
+
assertDeepInterviewInputWithinLimit(customInput, MAX_USER_RESPONSE_LENGTH, "user_response");
|
|
1394
1487
|
if (activeRemoteReceipt) {
|
|
1395
1488
|
const settlement: AskSettlement =
|
|
1396
1489
|
clarificationQuestion !== undefined
|
|
@@ -1425,10 +1518,14 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1425
1518
|
timedOut,
|
|
1426
1519
|
};
|
|
1427
1520
|
} catch (error) {
|
|
1428
|
-
await settleActiveRemote(
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1521
|
+
await settleActiveRemote(
|
|
1522
|
+
error instanceof Error && error.message.includes("exceeds max length")
|
|
1523
|
+
? { kind: "invalid", reason: "invalid_structured_answer" }
|
|
1524
|
+
: {
|
|
1525
|
+
kind: "resolve_without_commit",
|
|
1526
|
+
reason: error instanceof Error && error.name === "AbortError" ? "aborted" : "exception",
|
|
1527
|
+
},
|
|
1528
|
+
);
|
|
1432
1529
|
activeRemoteRequest = undefined;
|
|
1433
1530
|
if (error instanceof Error && error.name === "AbortError") {
|
|
1434
1531
|
throw new ToolAbortError("Ask input was cancelled");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
2
|
import * as path from "node:path";
|
|
3
3
|
import type { Skill } from "../extensibility/skills";
|
|
4
|
-
import { type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
4
|
+
import { initializeLocalRoot, type LocalProtocolOptions, resolveLocalUrlToPath } from "../internal-urls";
|
|
5
5
|
import { validateRelativePath } from "../internal-urls/skill-protocol";
|
|
6
6
|
import type { InternalResource } from "../internal-urls/types";
|
|
7
7
|
import { normalizeLocalScheme } from "./path-utils";
|
|
@@ -162,12 +162,23 @@ async function resolveInternalUrlToPath(
|
|
|
162
162
|
return resolveSkillUrlToPath(url, skills);
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
+
// Managed artifact and agent resources carry session authority. Expanding
|
|
166
|
+
// either into a subprocess command would let that subprocess reopen, replace,
|
|
167
|
+
// or unlink the managed object by pathname. Keep those resources behind the
|
|
168
|
+
// internal reader boundary.
|
|
169
|
+
if (scheme === "artifact" || scheme === "agent") {
|
|
170
|
+
throw new ToolError(
|
|
171
|
+
`${scheme}:// URLs cannot be expanded in bash commands; read them through the internal reader instead.`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
|
|
165
175
|
if (scheme === "local") {
|
|
166
176
|
if (!localOptions) {
|
|
167
177
|
throw new ToolError(
|
|
168
178
|
"Cannot resolve local:// URL in bash command: local protocol options are unavailable for this session.",
|
|
169
179
|
);
|
|
170
180
|
}
|
|
181
|
+
await initializeLocalRoot(localOptions);
|
|
171
182
|
const resolvedLocalPath = resolveLocalUrlToPath(url, localOptions);
|
|
172
183
|
if (ensureLocalParentDirs) {
|
|
173
184
|
await fs.mkdir(path.dirname(resolvedLocalPath), { recursive: true });
|
|
@@ -215,8 +226,8 @@ export function expandSkillUrls(command: string, skills: readonly Skill[]): stri
|
|
|
215
226
|
}
|
|
216
227
|
|
|
217
228
|
/**
|
|
218
|
-
* Expand
|
|
219
|
-
*
|
|
229
|
+
* Expand filesystem-safe internal URLs in a bash command string. `agent://` and
|
|
230
|
+
* `artifact://` remain internal-reader-only and are rejected before resolution.
|
|
220
231
|
*/
|
|
221
232
|
export async function expandInternalUrls(command: string, options: InternalUrlExpansionOptions): Promise<string> {
|
|
222
233
|
if (!command.includes("://") && !command.includes("local:/")) return command;
|
package/src/tools/bash.ts
CHANGED
|
@@ -636,6 +636,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
636
636
|
internalRouter: InternalUrlRouter.instance(),
|
|
637
637
|
localOptions: {
|
|
638
638
|
getArtifactsDir: this.session.getArtifactsDir,
|
|
639
|
+
isManagedDestination: this.session.isManagedSessionDestination,
|
|
639
640
|
getSessionId: this.session.getSessionId,
|
|
640
641
|
},
|
|
641
642
|
};
|
|
@@ -643,7 +644,6 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
643
644
|
...internalUrlOptions,
|
|
644
645
|
ensureLocalParentDirs: this.session.bashRestrictionProfile !== "read-only",
|
|
645
646
|
});
|
|
646
|
-
const sessionFile = this.session.getSessionFile?.() ?? null;
|
|
647
647
|
const expandedEnv = env
|
|
648
648
|
? Object.fromEntries(
|
|
649
649
|
await Promise.all(
|
|
@@ -662,7 +662,7 @@ export class BashTool implements AgentTool<BashToolSchema, BashToolDetails> {
|
|
|
662
662
|
: undefined;
|
|
663
663
|
const resolvedEnv = {
|
|
664
664
|
...buildGjcRuntimeSessionEnv({
|
|
665
|
-
sessionFile,
|
|
665
|
+
sessionFile: null,
|
|
666
666
|
sessionId: this.session.getSessionId?.(),
|
|
667
667
|
cwd: this.session.cwd,
|
|
668
668
|
}),
|
package/src/tools/index.ts
CHANGED
|
@@ -216,6 +216,8 @@ export interface ToolSession {
|
|
|
216
216
|
cwd: string;
|
|
217
217
|
/** Whether UI is available */
|
|
218
218
|
hasUI: boolean;
|
|
219
|
+
/** Whether this session will bind a workflow-gate emitter after tool construction. */
|
|
220
|
+
workflowGateEligible?: boolean;
|
|
219
221
|
/** Skip Python kernel availability check and warmup */
|
|
220
222
|
skipPythonPreflight?: boolean;
|
|
221
223
|
/** Pre-loaded context files (AGENTS.md, etc) */
|
|
@@ -262,6 +264,8 @@ export interface ToolSession {
|
|
|
262
264
|
requestForegroundBashBackground?: () => boolean;
|
|
263
265
|
/** Get session ID */
|
|
264
266
|
getSessionId?: () => string | null;
|
|
267
|
+
/** Whether local:// must use external managed scratch instead of artifacts/local. */
|
|
268
|
+
isManagedSessionDestination?: () => boolean;
|
|
265
269
|
/** Get Hindsight runtime state for this agent session. */
|
|
266
270
|
getHindsightSessionState?: () => HindsightSessionState | undefined;
|
|
267
271
|
/** Agent identity used for IRC routing. Returns the registry id (e.g. "0-Main", "0-AuthLoader"). */
|
|
@@ -11,6 +11,7 @@ function resolveRawPath(session: ToolSession, targetPath: string): string {
|
|
|
11
11
|
if (normalized.startsWith(LOCAL_SCHEME_PREFIX)) {
|
|
12
12
|
return resolveLocalUrlToPath(normalized, {
|
|
13
13
|
getArtifactsDir: session.getArtifactsDir,
|
|
14
|
+
isManagedDestination: session.isManagedSessionDestination,
|
|
14
15
|
getSessionId: session.getSessionId,
|
|
15
16
|
});
|
|
16
17
|
}
|
|
@@ -12,6 +12,7 @@ const MAX_QUERY_LIMIT = 500;
|
|
|
12
12
|
const MAX_RENDER_WIDTH = 120;
|
|
13
13
|
const MAX_COLUMN_WIDTH = 40;
|
|
14
14
|
const MIN_COLUMN_WIDTH = 1;
|
|
15
|
+
const SQLITE_USER_TABLE_PREDICATE = "name NOT LIKE 'sqlite!_%' ESCAPE '!'";
|
|
15
16
|
|
|
16
17
|
type SqliteBinding = Exclude<SQLQueryBindings, Record<string, unknown>>;
|
|
17
18
|
|
|
@@ -182,7 +183,7 @@ function getTableMasterRow(db: Database, table: string): SqliteMasterRow {
|
|
|
182
183
|
const row =
|
|
183
184
|
db
|
|
184
185
|
.prepare<SqliteMasterRow, [string]>(
|
|
185
|
-
|
|
186
|
+
`SELECT name, sql FROM sqlite_master WHERE type = 'table' AND ${SQLITE_USER_TABLE_PREDICATE} AND name = ?`,
|
|
186
187
|
)
|
|
187
188
|
.get(table) ?? null;
|
|
188
189
|
if (!row) {
|
|
@@ -502,7 +503,7 @@ export function parseSqliteSelector(subPath: string, queryString: string): Sqlit
|
|
|
502
503
|
export function listTables(db: Database): { name: string; rowCount: number }[] {
|
|
503
504
|
const names = db
|
|
504
505
|
.prepare<Pick<SqliteMasterRow, "name">, []>(
|
|
505
|
-
|
|
506
|
+
`SELECT name FROM sqlite_master WHERE type = 'table' AND ${SQLITE_USER_TABLE_PREDICATE} ORDER BY name COLLATE NOCASE`,
|
|
506
507
|
)
|
|
507
508
|
.all();
|
|
508
509
|
|
package/src/tools/subagent.ts
CHANGED
|
@@ -227,6 +227,8 @@ export class SubagentTool implements AgentTool<typeof subagentSchema, SubagentTo
|
|
|
227
227
|
if (!result.ok && result.reason === "context_unavailable") throw new ToolError("context unavailable");
|
|
228
228
|
if (!result.ok && result.reason === "not_found") {
|
|
229
229
|
missing.push(this.#missingSnapshot(id, "not_found", "No visible detached subagent matches this id."));
|
|
230
|
+
} else if (!result.ok) {
|
|
231
|
+
throw new ToolError(`Failed to resume subagent ${record.subagentId}: ${result.reason ?? "unknown"}.`);
|
|
230
232
|
} else {
|
|
231
233
|
records.push(manager.getSubagentRecord(record.subagentId, ownerFilter) ?? record);
|
|
232
234
|
}
|