@gajae-code/coding-agent 0.11.7 → 0.11.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/dist/types/cli/args.d.ts +0 -2
- package/dist/types/config/keybindings.d.ts +33 -3
- package/dist/types/config/model-profile-activation.d.ts +4 -3
- package/dist/types/config/model-profile-contract.d.ts +38 -0
- package/dist/types/config/settings-schema.d.ts +15 -2
- package/dist/types/edit/modes/patch.d.ts +13 -0
- package/dist/types/edit/path-mutation-lock.d.ts +15 -0
- package/dist/types/eval/py/executor.d.ts +1 -1
- package/dist/types/eval/py/tool-bridge.d.ts +1 -2
- package/dist/types/gjc-runtime/repository-binding.d.ts +63 -0
- package/dist/types/gjc-runtime/ultragoal-guard.d.ts +1 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +24 -0
- package/dist/types/main.d.ts +8 -0
- package/dist/types/modes/components/runtime-mcp-add-wizard.d.ts +2 -1
- package/dist/types/modes/components/tool-status-header.d.ts +3 -2
- package/dist/types/modes/components/welcome.d.ts +2 -0
- package/dist/types/modes/controllers/command-controller.d.ts +2 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +2 -1
- package/dist/types/modes/interactive-mode.d.ts +5 -2
- package/dist/types/modes/prompt-action-autocomplete.d.ts +6 -1
- package/dist/types/modes/prompt-suggestion-controller.d.ts +35 -0
- package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +2 -2
- package/dist/types/modes/types.d.ts +3 -0
- package/dist/types/modes/utils/hotkeys-markdown.d.ts +2 -1
- package/dist/types/runtime-mcp/manager.d.ts +1 -1
- package/dist/types/runtime-mcp/oauth-flow.d.ts +0 -5
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +14 -0
- package/dist/types/sdk/broker/broker.d.ts +2 -0
- package/dist/types/sdk/broker/lifecycle.d.ts +6 -0
- package/dist/types/sdk/bus/chat-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/config-commands.d.ts +2 -2
- package/dist/types/sdk/bus/config.d.ts +2 -0
- package/dist/types/sdk/bus/conversation-store.d.ts +1 -0
- package/dist/types/sdk/bus/index.d.ts +25 -0
- package/dist/types/sdk/bus/notification-orchestration.d.ts +1 -0
- package/dist/types/sdk/bus/prompt-reconciliation.d.ts +105 -0
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +13 -3
- package/dist/types/sdk/bus/telegram-daemon-control.d.ts +5 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +128 -1
- package/dist/types/sdk/bus/telegram-reference.d.ts +2 -0
- package/dist/types/sdk/host/control/operations.d.ts +1 -1
- package/dist/types/sdk/host/host.d.ts +1 -1
- package/dist/types/sdk/host/query/handlers.d.ts +9 -0
- package/dist/types/sdk/index.d.ts +2 -0
- package/dist/types/sdk/prompt-status.d.ts +88 -0
- package/dist/types/sdk/session-directory.d.ts +1 -1
- package/dist/types/sdk/startup-capability.d.ts +3 -0
- package/dist/types/session/agent-session.d.ts +2 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +11 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +17 -2
- package/dist/types/session/internal/native-publish-outcome.d.ts +48 -0
- package/dist/types/session/session-storage.d.ts +18 -0
- package/dist/types/task/index.d.ts +3 -1
- package/dist/types/task/receipt.d.ts +2 -0
- package/dist/types/task/types.d.ts +94 -0
- package/dist/types/tools/ask.d.ts +454 -174
- package/dist/types/tools/image-gen.d.ts +47 -1
- package/dist/types/tools/index.d.ts +4 -0
- package/dist/types/utils/prompt-suggestion.d.ts +33 -0
- package/package.json +8 -8
- package/scripts/capture-platform-shortcut-labels-showcase.ts +342 -0
- package/scripts/dogfood-repository-binding.ts +209 -0
- package/scripts/generate-sdk-operation-inventory.ts +2 -0
- package/scripts/run-test-manifest.ts +4 -4
- package/src/cli/args.ts +2 -92
- package/src/cli/notify-cli.ts +5 -38
- package/src/cli.ts +18 -0
- package/src/commands/sdk.ts +2 -2
- package/src/config/keybindings.ts +157 -24
- package/src/config/model-profile-activation.ts +7 -18
- package/src/config/model-profile-contract.ts +161 -0
- package/src/config/model-registry.ts +11 -1
- package/src/config/settings-schema.ts +17 -2
- package/src/coordinator-mcp/model-preset.ts +21 -74
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +2 -2
- package/src/defaults/gjc/skills/ralplan/SKILL.md +3 -1
- package/src/edit/index.ts +3 -6
- package/src/edit/modes/patch.ts +38 -1
- package/src/edit/modes/replace.ts +42 -0
- package/src/edit/path-mutation-lock.ts +112 -0
- package/src/eval/py/executor.ts +30 -11
- package/src/eval/py/prelude.py +2 -2
- package/src/eval/py/tool-bridge.ts +34 -18
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +2 -1
- package/src/gjc-runtime/launch-tmux.ts +1 -1
- package/src/gjc-runtime/managed-owner-supervisor.ts +83 -4
- package/src/gjc-runtime/ralplan-runtime.ts +75 -3
- package/src/gjc-runtime/repository-binding.ts +267 -0
- package/src/gjc-runtime/session-state-sidecar.ts +25 -9
- package/src/gjc-runtime/tmux-owner-isolation.ts +2 -1
- package/src/gjc-runtime/tmux-sessions.ts +45 -6
- package/src/gjc-runtime/ultragoal-guard.ts +5 -5
- package/src/gjc-runtime/ultragoal-runtime.ts +181 -19
- package/src/internal-urls/docs-index.generated.ts +6 -5
- package/src/internal-urls/local-protocol.ts +33 -16
- package/src/internal-urls/mcp-protocol.ts +73 -11
- package/src/main.ts +25 -10
- package/src/modes/DESIGN.md +5 -0
- package/src/modes/acp/acp-agent.ts +50 -16
- package/src/modes/components/model-selector.ts +42 -8
- package/src/modes/components/plan-preview-overlay.ts +1 -1
- package/src/modes/components/runtime-mcp-add-wizard.ts +5 -0
- package/src/modes/components/tool-status-header.ts +20 -4
- package/src/modes/components/welcome.ts +22 -18
- package/src/modes/controllers/command-controller.ts +97 -54
- package/src/modes/controllers/event-controller.ts +10 -0
- package/src/modes/controllers/input-controller.ts +7 -2
- package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -6
- package/src/modes/controllers/selector-controller.ts +46 -54
- package/src/modes/interactive-mode.ts +41 -27
- package/src/modes/prompt-action-autocomplete.ts +32 -13
- package/src/modes/prompt-suggestion-controller.ts +96 -0
- package/src/modes/shared/agent-wire/workflow-gate-broker.ts +2 -2
- package/src/modes/types.ts +3 -0
- package/src/modes/utils/hotkeys-markdown.ts +64 -36
- package/src/modes/utils/ui-helpers.ts +5 -3
- package/src/prompts/agents/architect.md +0 -1
- package/src/prompts/agents/critic.md +0 -1
- package/src/prompts/system/prompt-suggestion-system.md +32 -0
- package/src/prompts/system/system-prompt.md +6 -4
- package/src/prompts/tools/cron.md +3 -1
- package/src/runtime-mcp/manager.ts +51 -53
- package/src/runtime-mcp/oauth-flow.ts +0 -72
- package/src/runtime-mcp/plugin-network-boundary.ts +101 -0
- package/src/runtime-mcp/transports/http.ts +13 -6
- package/src/sdk/broker/broker.ts +34 -20
- package/src/sdk/broker/lifecycle.ts +185 -36
- package/src/sdk/bus/chat-daemon-control.ts +18 -5
- package/src/sdk/bus/config-commands.ts +2 -2
- package/src/sdk/bus/config.ts +9 -1
- package/src/sdk/bus/conversation-store.ts +40 -6
- package/src/sdk/bus/index.ts +240 -65
- package/src/sdk/bus/lifecycle-commands.ts +47 -3
- package/src/sdk/bus/lifecycle-control-runtime.ts +11 -2
- package/src/sdk/bus/lifecycle-orchestrator.ts +32 -1
- package/src/sdk/bus/notification-orchestration.ts +12 -0
- package/src/sdk/bus/notification-service.ts +27 -2
- package/src/sdk/bus/prompt-reconciliation.ts +239 -0
- package/src/sdk/bus/recent-activity.ts +90 -26
- package/src/sdk/bus/telegram-daemon-contract.ts +13 -3
- package/src/sdk/bus/telegram-daemon-control.ts +145 -33
- package/src/sdk/bus/telegram-daemon.ts +835 -110
- package/src/sdk/bus/telegram-reference.ts +20 -2
- package/src/sdk/cli/session-cli.ts +15 -2
- package/src/sdk/host/control/dispatch.ts +1 -1
- package/src/sdk/host/control/operations.ts +1 -1
- package/src/sdk/host/host.ts +2 -6
- package/src/sdk/host/query/handlers.ts +70 -5
- package/src/sdk/index.ts +12 -0
- package/src/sdk/prompt-status.ts +97 -0
- package/src/sdk/protocol/operation-inventory.generated.json +47 -0
- package/src/sdk/protocol/operation-registry.ts +18 -4
- package/src/sdk/session-directory.ts +5 -1
- package/src/sdk/session.ts +2 -0
- package/src/sdk/startup-capability.ts +9 -1
- package/src/session/agent-session.ts +52 -5
- package/src/session/blob-store.ts +7 -2
- package/src/session/internal/managed-session-scope.ts +854 -84
- package/src/session/internal/managed-session-storage.ts +251 -199
- package/src/session/internal/native-publish-outcome.ts +266 -0
- package/src/session/session-manager.ts +310 -327
- package/src/session/session-storage.ts +128 -11
- package/src/task/gjc-command.ts +2 -2
- package/src/task/index.ts +121 -15
- package/src/task/receipt.ts +3 -0
- package/src/task/types.ts +34 -3
- package/src/tools/ask.ts +198 -88
- package/src/tools/ast-edit.ts +2 -2
- package/src/tools/bash-pty-selection.ts +2 -2
- package/src/tools/browser/screenshot-format.ts +6 -1
- package/src/tools/cron.ts +1 -1
- package/src/tools/image-gen.ts +178 -6
- package/src/tools/index.ts +4 -0
- package/src/tools/render-utils.ts +7 -1
- package/src/utils/edit-mode.ts +2 -2
- package/src/utils/prompt-suggestion.ts +265 -0
- package/src/utils/sixel.ts +3 -3
- package/src/web/insane/url-guard.ts +3 -1
- package/src/web/scrapers/docs-rs.ts +30 -5
package/src/tools/ask.ts
CHANGED
|
@@ -160,20 +160,23 @@ const DeepInterviewMetadata = z.object({
|
|
|
160
160
|
.optional(),
|
|
161
161
|
});
|
|
162
162
|
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
163
|
+
const DeepInterviewTopologyMeta = DeepInterviewMetadata.extend({
|
|
164
|
+
round: z.literal(0).describe("Round 0 topology confirmation"),
|
|
165
|
+
component: z.literal("review-topology"),
|
|
166
|
+
dimension: z.literal("topology"),
|
|
167
|
+
intent_contract: DeepInterviewIntentContract.describe("required Round 0 locked-intent contract"),
|
|
168
|
+
}).strict();
|
|
169
|
+
|
|
170
|
+
const DeepInterviewRoundMeta = DeepInterviewMetadata.extend({
|
|
171
|
+
round: z.number().int().positive().describe("positive interview round number"),
|
|
172
|
+
}).strict();
|
|
173
|
+
|
|
174
|
+
const DeepInterviewReviewMeta = DeepInterviewMetadata.extend({
|
|
175
|
+
round: z.number().int().positive().describe("positive post-Round-0 review number"),
|
|
176
|
+
intent_review: DeepInterviewIntentReview.describe("post-Round-0 locked-intent reduction review"),
|
|
177
|
+
}).strict();
|
|
178
|
+
|
|
179
|
+
const DeepInterviewMeta = z.union([DeepInterviewTopologyMeta, DeepInterviewRoundMeta, DeepInterviewReviewMeta]);
|
|
177
180
|
type DeepInterviewMeta = z.infer<typeof DeepInterviewMeta>;
|
|
178
181
|
|
|
179
182
|
function intentContract(
|
|
@@ -191,81 +194,118 @@ const WorkflowGateMeta = z.object({
|
|
|
191
194
|
kind: z.enum(["question", "approval", "execution"]).describe("workflow gate kind"),
|
|
192
195
|
});
|
|
193
196
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
message: "intent confirmation options must be unique",
|
|
238
|
-
path: ["deepInterview", "intent_contract"],
|
|
239
|
-
});
|
|
240
|
-
const approvalOptions = review?.approval_options ?? [];
|
|
241
|
-
if (new Set(approvalOptions).size !== approvalOptions.length)
|
|
242
|
-
context.addIssue({
|
|
243
|
-
code: "custom",
|
|
244
|
-
message: "intent approval options must be unique",
|
|
245
|
-
path: ["deepInterview", "intent_review"],
|
|
246
|
-
});
|
|
247
|
-
for (const label of confirmationOptions) {
|
|
248
|
-
if (!labels.has(label))
|
|
197
|
+
function createQuestionItemSchema(deepInterviewSchema: z.ZodType<DeepInterviewMeta>) {
|
|
198
|
+
return z
|
|
199
|
+
.object({
|
|
200
|
+
id: z.string().describe("question id"),
|
|
201
|
+
question: z.string().describe("question text"),
|
|
202
|
+
options: z.array(OptionItem).describe("available options"),
|
|
203
|
+
multi: z.boolean().describe("allow multiple selections").optional(),
|
|
204
|
+
recommended: z.number().describe("recommended option index").optional(),
|
|
205
|
+
deepInterview: deepInterviewSchema.describe("optional deep-interview round metadata").optional(),
|
|
206
|
+
workflowGate: WorkflowGateMeta.describe("optional workflow gate stage/kind override").optional(),
|
|
207
|
+
})
|
|
208
|
+
.superRefine((value, context) => {
|
|
209
|
+
const labels = new Set(value.options.map(option => option.label));
|
|
210
|
+
const contract = intentContract(value.deepInterview);
|
|
211
|
+
const review = intentReview(value.deepInterview);
|
|
212
|
+
if (
|
|
213
|
+
value.deepInterview &&
|
|
214
|
+
value.workflowGate &&
|
|
215
|
+
(value.workflowGate.stage !== "deep-interview" || value.workflowGate.kind !== "question")
|
|
216
|
+
)
|
|
217
|
+
context.addIssue({
|
|
218
|
+
code: "custom",
|
|
219
|
+
message: "deep-interview metadata requires a deep-interview question workflow gate",
|
|
220
|
+
path: ["workflowGate"],
|
|
221
|
+
});
|
|
222
|
+
if (contract && review)
|
|
223
|
+
context.addIssue({
|
|
224
|
+
code: "custom",
|
|
225
|
+
message: "intent contract and review are mutually exclusive",
|
|
226
|
+
path: ["deepInterview"],
|
|
227
|
+
});
|
|
228
|
+
if (
|
|
229
|
+
contract &&
|
|
230
|
+
(value.deepInterview?.round !== 0 ||
|
|
231
|
+
value.deepInterview.component !== "review-topology" ||
|
|
232
|
+
value.deepInterview.dimension !== "topology")
|
|
233
|
+
)
|
|
234
|
+
context.addIssue({
|
|
235
|
+
code: "custom",
|
|
236
|
+
message: "intent contract requires round-0 review topology metadata",
|
|
237
|
+
path: ["deepInterview"],
|
|
238
|
+
});
|
|
239
|
+
if (review && (value.deepInterview?.round ?? 0) <= 0)
|
|
249
240
|
context.addIssue({
|
|
250
241
|
code: "custom",
|
|
251
|
-
message: "intent
|
|
242
|
+
message: "intent review requires a positive round",
|
|
243
|
+
path: ["deepInterview", "round"],
|
|
244
|
+
});
|
|
245
|
+
if ((contract || review) && value.multi === true)
|
|
246
|
+
context.addIssue({ code: "custom", message: "intent gates must be single-select", path: ["multi"] });
|
|
247
|
+
const confirmationOptions = contract?.confirmation_options ?? [];
|
|
248
|
+
if (new Set(confirmationOptions).size !== confirmationOptions.length)
|
|
249
|
+
context.addIssue({
|
|
250
|
+
code: "custom",
|
|
251
|
+
message: "intent confirmation options must be unique",
|
|
252
252
|
path: ["deepInterview", "intent_contract"],
|
|
253
253
|
});
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
if (!labels.has(label))
|
|
254
|
+
const approvalOptions = review?.approval_options ?? [];
|
|
255
|
+
if (new Set(approvalOptions).size !== approvalOptions.length)
|
|
257
256
|
context.addIssue({
|
|
258
257
|
code: "custom",
|
|
259
|
-
message: "intent approval
|
|
258
|
+
message: "intent approval options must be unique",
|
|
260
259
|
path: ["deepInterview", "intent_review"],
|
|
261
260
|
});
|
|
262
|
-
|
|
263
|
-
|
|
261
|
+
for (const label of confirmationOptions) {
|
|
262
|
+
if (!labels.has(label))
|
|
263
|
+
context.addIssue({
|
|
264
|
+
code: "custom",
|
|
265
|
+
message: "intent confirmation option must be displayed",
|
|
266
|
+
path: ["deepInterview", "intent_contract"],
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
for (const label of approvalOptions) {
|
|
270
|
+
if (!labels.has(label))
|
|
271
|
+
context.addIssue({
|
|
272
|
+
code: "custom",
|
|
273
|
+
message: "intent approval option must be displayed",
|
|
274
|
+
path: ["deepInterview", "intent_review"],
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const QuestionItem = createQuestionItemSchema(DeepInterviewMeta);
|
|
281
|
+
const TopologyQuestionItem = createQuestionItemSchema(DeepInterviewTopologyMeta);
|
|
282
|
+
const PostTopologyQuestionItem = createQuestionItemSchema(z.union([DeepInterviewRoundMeta, DeepInterviewReviewMeta]));
|
|
283
|
+
|
|
284
|
+
const OrdinaryQuestionItem = z.object({
|
|
285
|
+
id: z.string().describe("question id"),
|
|
286
|
+
question: z.string().describe("question text"),
|
|
287
|
+
options: z.array(OptionItem).describe("available options"),
|
|
288
|
+
multi: z.boolean().describe("allow multiple selections").optional(),
|
|
289
|
+
recommended: z.number().describe("recommended option index").optional(),
|
|
290
|
+
workflowGate: WorkflowGateMeta.describe("optional workflow gate stage/kind override").optional(),
|
|
291
|
+
});
|
|
264
292
|
|
|
265
293
|
export const askSchema = z.object({
|
|
266
294
|
questions: z.array(QuestionItem).min(1).describe("questions to ask"),
|
|
267
295
|
});
|
|
268
296
|
|
|
297
|
+
const topologyAskSchema = z.object({
|
|
298
|
+
questions: z.array(TopologyQuestionItem).min(1).describe("questions to ask"),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const postTopologyAskSchema = z.object({
|
|
302
|
+
questions: z.array(PostTopologyQuestionItem).min(1).describe("questions to ask"),
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
const ordinaryAskSchema = z.object({
|
|
306
|
+
questions: z.array(OrdinaryQuestionItem).min(1).describe("questions to ask"),
|
|
307
|
+
});
|
|
308
|
+
|
|
269
309
|
export type AskToolInput = z.infer<typeof askSchema>;
|
|
270
310
|
|
|
271
311
|
function isPlainRecord(value: unknown): value is Record<string, unknown> {
|
|
@@ -361,9 +401,41 @@ function normalizeRoundZeroOptionalNulls(arguments_: Record<string, unknown>): R
|
|
|
361
401
|
if (changed) normalizedQuestion.deepInterview = normalizedDeepInterview;
|
|
362
402
|
return changed ? { ...arguments_, questions: [normalizedQuestion] } : arguments_;
|
|
363
403
|
}
|
|
364
|
-
|
|
404
|
+
|
|
405
|
+
function knownIntentRejection(arguments_: Record<string, unknown>): RawArgumentValidationResult | undefined {
|
|
406
|
+
if (!isPlainRecord(arguments_) || !Array.isArray(arguments_.questions) || arguments_.questions.length !== 1)
|
|
407
|
+
return undefined;
|
|
408
|
+
const question = arguments_.questions[0];
|
|
409
|
+
if (!isPlainRecord(question) || !isPlainRecord(question.deepInterview)) return undefined;
|
|
410
|
+
const metadata = question.deepInterview;
|
|
411
|
+
const hasContract = Object.hasOwn(metadata, "intent_contract");
|
|
412
|
+
const hasReview = Object.hasOwn(metadata, "intent_review");
|
|
413
|
+
const workflowGate = question.workflowGate;
|
|
414
|
+
if (
|
|
415
|
+
Object.hasOwn(question, "workflowGate") &&
|
|
416
|
+
(!isPlainRecord(workflowGate) || workflowGate.stage !== "deep-interview" || workflowGate.kind !== "question")
|
|
417
|
+
)
|
|
418
|
+
return { outcome: "reject", code: "ask-deep-interview-metadata-requires-deep-interview-gate" };
|
|
419
|
+
if (hasReview && !hasContract && metadata.round === 0) {
|
|
420
|
+
return { outcome: "reject", code: "ask-intent-review-requires-positive-round" };
|
|
421
|
+
}
|
|
422
|
+
if (!hasContract || !isPlainRecord(metadata.intent_contract)) return undefined;
|
|
423
|
+
const contract = metadata.intent_contract;
|
|
424
|
+
if (
|
|
425
|
+
(Array.isArray(contract.items) && contract.items.length === 0) ||
|
|
426
|
+
(Array.isArray(contract.confirmation_options) && contract.confirmation_options.length === 0)
|
|
427
|
+
)
|
|
428
|
+
return { outcome: "reject", code: "ask-intent-contract-requires-non-empty-authority" };
|
|
429
|
+
return undefined;
|
|
430
|
+
}
|
|
431
|
+
function recoverRoundZeroIntentContract(
|
|
432
|
+
arguments_: Record<string, unknown>,
|
|
433
|
+
stage?: "topology" | "post-topology",
|
|
434
|
+
): RawArgumentValidationResult {
|
|
365
435
|
if (!isRoundZeroRecoveryCandidate(arguments_)) return { outcome: "passthrough" };
|
|
366
436
|
const normalizedArguments = normalizeRoundZeroOptionalNulls(arguments_);
|
|
437
|
+
const knownRejection = knownIntentRejection(normalizedArguments);
|
|
438
|
+
if (knownRejection) return knownRejection;
|
|
367
439
|
if (!isOnlyPlainData(normalizedArguments) || !isPlainRecord(normalizedArguments)) return { outcome: "reject" };
|
|
368
440
|
if (
|
|
369
441
|
!hasExactOwnKeys(normalizedArguments, ["questions"]) ||
|
|
@@ -421,10 +493,19 @@ function recoverRoundZeroIntentContract(arguments_: Record<string, unknown>): Ra
|
|
|
421
493
|
"intent_contract",
|
|
422
494
|
"intent_review",
|
|
423
495
|
];
|
|
496
|
+
if (!hasOnlyAllowedOwnKeys(deepInterview, deepInterviewKeys)) return { outcome: "reject" };
|
|
497
|
+
if (
|
|
498
|
+
stage === "post-topology" &&
|
|
499
|
+
!hasIntentContract &&
|
|
500
|
+
hasIntentReview &&
|
|
501
|
+
typeof deepInterview.round === "number" &&
|
|
502
|
+
Number.isInteger(deepInterview.round) &&
|
|
503
|
+
deepInterview.round > 0
|
|
504
|
+
)
|
|
505
|
+
return { outcome: "passthrough" };
|
|
424
506
|
if (
|
|
425
|
-
!
|
|
426
|
-
!
|
|
427
|
-
!Object.hasOwn(deepInterview, "intent_review") ||
|
|
507
|
+
!hasIntentContract ||
|
|
508
|
+
!hasIntentReview ||
|
|
428
509
|
deepInterview.round !== 0 ||
|
|
429
510
|
typeof deepInterview.component !== "string" ||
|
|
430
511
|
deepInterview.component !== "review-topology" ||
|
|
@@ -522,6 +603,16 @@ function getDoneOptionLabel(): string {
|
|
|
522
603
|
return `${theme.status.success} Done selecting`;
|
|
523
604
|
}
|
|
524
605
|
|
|
606
|
+
function validRecommendedIndex(recommended: number | undefined, optionCount: number): number | undefined {
|
|
607
|
+
return typeof recommended === "number" &&
|
|
608
|
+
Number.isFinite(recommended) &&
|
|
609
|
+
Number.isInteger(recommended) &&
|
|
610
|
+
recommended >= 0 &&
|
|
611
|
+
recommended < optionCount
|
|
612
|
+
? recommended
|
|
613
|
+
: undefined;
|
|
614
|
+
}
|
|
615
|
+
|
|
525
616
|
/** Add "(Recommended)" suffix to the option at the given index if not already present */
|
|
526
617
|
function addRecommendedSuffix(labels: string[], recommendedIndex?: number): string[] {
|
|
527
618
|
if (recommendedIndex === undefined || recommendedIndex < 0 || recommendedIndex >= labels.length) {
|
|
@@ -1050,6 +1141,11 @@ function formatQuestionResult(result: QuestionResult): string {
|
|
|
1050
1141
|
// =============================================================================
|
|
1051
1142
|
|
|
1052
1143
|
type AskParams = AskToolInput;
|
|
1144
|
+
type AskParametersSchema =
|
|
1145
|
+
| typeof ordinaryAskSchema
|
|
1146
|
+
| typeof askSchema
|
|
1147
|
+
| typeof topologyAskSchema
|
|
1148
|
+
| typeof postTopologyAskSchema;
|
|
1053
1149
|
|
|
1054
1150
|
/**
|
|
1055
1151
|
* Ask tool for interactive user prompting during execution.
|
|
@@ -1057,13 +1153,19 @@ type AskParams = AskToolInput;
|
|
|
1057
1153
|
* Allows gathering user preferences, clarifying instructions, and getting decisions
|
|
1058
1154
|
* on implementation choices as the agent works.
|
|
1059
1155
|
*/
|
|
1060
|
-
export class AskTool implements AgentTool<
|
|
1156
|
+
export class AskTool implements AgentTool<AskParametersSchema, AskToolDetails> {
|
|
1061
1157
|
readonly name = "ask";
|
|
1062
1158
|
readonly label = "Ask";
|
|
1063
1159
|
readonly summary = "Ask the user a clarifying question";
|
|
1064
1160
|
readonly description: string;
|
|
1065
|
-
|
|
1066
|
-
|
|
1161
|
+
get parameters(): AskParametersSchema {
|
|
1162
|
+
const stage = this.session.getDeepInterviewAskStage?.();
|
|
1163
|
+
if (stage === "topology") return topologyAskSchema;
|
|
1164
|
+
if (stage === "post-topology") return postTopologyAskSchema;
|
|
1165
|
+
return ordinaryAskSchema;
|
|
1166
|
+
}
|
|
1167
|
+
readonly rawArgumentValidation = (arguments_: Record<string, unknown>): RawArgumentValidationResult =>
|
|
1168
|
+
recoverRoundZeroIntentContract(arguments_, this.session.getDeepInterviewAskStage?.());
|
|
1067
1169
|
readonly strict = true;
|
|
1068
1170
|
readonly loadMode = "discoverable";
|
|
1069
1171
|
|
|
@@ -1098,6 +1200,8 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1098
1200
|
if (customInput !== undefined && (meta || isDeepInterviewAskQuestion(q.question)))
|
|
1099
1201
|
assertDeepInterviewInputWithinLimit(customInput, MAX_USER_RESPONSE_LENGTH, "user_response");
|
|
1100
1202
|
if (!meta) return;
|
|
1203
|
+
if (this.session.getDeepInterviewAskStage?.() === undefined) return;
|
|
1204
|
+
if (q.workflowGate && (q.workflowGate.stage !== "deep-interview" || q.workflowGate.kind !== "question")) return;
|
|
1101
1205
|
const cwd = this.session.cwd;
|
|
1102
1206
|
const sessionId = this.session.getSessionId?.() ?? undefined;
|
|
1103
1207
|
const statePath = deepInterviewStatePath(cwd, sessionId);
|
|
@@ -1154,12 +1258,13 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1154
1258
|
};
|
|
1155
1259
|
const gateEmitter = this.session.getWorkflowGateEmitter?.();
|
|
1156
1260
|
// A durable workflow-gate emitter now exists for every session, and its
|
|
1157
|
-
//
|
|
1158
|
-
// The workflow gate is only the headless (non-TUI) answer
|
|
1159
|
-
// interactive UI is present, prefer it — otherwise
|
|
1160
|
-
// route to emitGate() and hang forever waiting on
|
|
1261
|
+
// supportsRemoteGateAnswers() is always true, so it can no longer signal
|
|
1262
|
+
// "no local UI". The workflow gate is only the headless (non-TUI) answer
|
|
1263
|
+
// path: when a real interactive UI is present, prefer it — otherwise
|
|
1264
|
+
// attended TUI asks would route to emitGate() and hang forever waiting on
|
|
1265
|
+
// a remote responder.
|
|
1161
1266
|
const hasInteractiveUi = context?.hasUI === true && !!context.ui;
|
|
1162
|
-
const canUseWorkflowGate = !hasInteractiveUi && gateEmitter?.
|
|
1267
|
+
const canUseWorkflowGate = !hasInteractiveUi && gateEmitter?.supportsRemoteGateAnswers() === true;
|
|
1163
1268
|
// Headless fallback: SDK workflow gates are the non-TUI answer path.
|
|
1164
1269
|
if (!canUseWorkflowGate && (!context?.hasUI || !context.ui)) {
|
|
1165
1270
|
context?.abort();
|
|
@@ -1428,10 +1533,12 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1428
1533
|
}),
|
|
1429
1534
|
}
|
|
1430
1535
|
: options?.previous;
|
|
1536
|
+
const recommendedIndex = validRecommendedIndex(q.recommended, rawOptionLabels.length);
|
|
1431
1537
|
activeRemoteRequest = {
|
|
1432
1538
|
question: displayQuestion,
|
|
1433
1539
|
options: remoteSelectorOptions,
|
|
1434
1540
|
interaction: "selector",
|
|
1541
|
+
...(recommendedIndex === undefined ? {} : { recommendedIndex }),
|
|
1435
1542
|
controls: askRemoteControls({
|
|
1436
1543
|
multi: q.multi === true,
|
|
1437
1544
|
questionIndex,
|
|
@@ -1463,6 +1570,9 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
1463
1570
|
question: displayQuestion,
|
|
1464
1571
|
options: state.interaction === "selector" ? remoteSelectorOptions : [],
|
|
1465
1572
|
interaction: state.interaction,
|
|
1573
|
+
...(state.interaction === "selector" && recommendedIndex !== undefined
|
|
1574
|
+
? { recommendedIndex }
|
|
1575
|
+
: {}),
|
|
1466
1576
|
controls:
|
|
1467
1577
|
state.interaction === "selector"
|
|
1468
1578
|
? askRemoteControls({
|
package/src/tools/ast-edit.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallb
|
|
|
3
3
|
import { type AstReplaceChange, type AstReplaceFileChange, astEdit } from "@gajae-code/natives";
|
|
4
4
|
import type { Component } from "@gajae-code/tui";
|
|
5
5
|
import { Text } from "@gajae-code/tui";
|
|
6
|
-
import { $
|
|
6
|
+
import { $pickenvpos, prompt, untilAborted } from "@gajae-code/utils";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
8
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
9
9
|
import { computeLineHash, HL_BODY_SEP } from "../hashline/hash";
|
|
@@ -199,7 +199,7 @@ export class AstEditTool implements AgentTool<typeof astEditSchema, AstEditToolD
|
|
|
199
199
|
seenPatterns.add(pat);
|
|
200
200
|
}
|
|
201
201
|
const normalizedRewrites = Object.fromEntries(ops);
|
|
202
|
-
const maxFiles = $
|
|
202
|
+
const maxFiles = $pickenvpos(["GJC_MAX_AST_FILES", "PI_MAX_AST_FILES"], 1000);
|
|
203
203
|
|
|
204
204
|
const scope = await resolveToolSearchScope({
|
|
205
205
|
rawPaths: params.paths,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $
|
|
1
|
+
import { $pickenv } from "@gajae-code/utils/env";
|
|
2
2
|
|
|
3
3
|
/** Minimal UI-capability fields needed to decide whether bash can use the local PTY overlay. */
|
|
4
4
|
export interface BashPtyContext {
|
|
@@ -9,6 +9,6 @@ export interface BashPtyContext {
|
|
|
9
9
|
/** Return whether a bash tool call should use the local interactive PTY overlay. */
|
|
10
10
|
export function canUseInteractiveBashPty(pty: boolean, ctx: BashPtyContext | undefined): boolean {
|
|
11
11
|
if (!pty) return false;
|
|
12
|
-
if ($
|
|
12
|
+
if ($pickenv("GJC_NO_PTY", "PI_NO_PTY") === "1") return false;
|
|
13
13
|
return ctx?.hasUI === true && ctx.ui !== undefined;
|
|
14
14
|
}
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import * as os from "node:os";
|
|
2
|
+
import * as path from "node:path";
|
|
2
3
|
|
|
3
4
|
import { formatDimensionNote, type ResizedImage } from "../../utils/image-resize";
|
|
4
5
|
|
|
5
6
|
function shortenPath(filePath: string): string {
|
|
6
7
|
const home = os.homedir();
|
|
7
|
-
|
|
8
|
+
if (!home) return filePath;
|
|
9
|
+
// Require a separator boundary so a sibling that shares the home prefix
|
|
10
|
+
// (e.g. "/home/woodyx/x" for home "/home/woody") is not corrupted into "~x/x".
|
|
11
|
+
const boundary = home.endsWith(path.sep) ? home : home + path.sep;
|
|
12
|
+
return filePath === home || filePath.startsWith(boundary) ? `~${filePath.slice(home.length)}` : filePath;
|
|
8
13
|
}
|
|
9
14
|
|
|
10
15
|
export function formatScreenshot(opts: {
|
package/src/tools/cron.ts
CHANGED
|
@@ -36,7 +36,7 @@ const cronSchema = z
|
|
|
36
36
|
.string()
|
|
37
37
|
.optional()
|
|
38
38
|
.describe(
|
|
39
|
-
"(op=create, required) Prompt to inject between turns when the cron fires.
|
|
39
|
+
"(op=create, required) Prompt to inject between turns when the cron fires. Every firing starts a normal agent turn whose response may be visible; use a persistent monitor, not cron, for ongoing polling that should emit only on state changes.",
|
|
40
40
|
),
|
|
41
41
|
recurring: z
|
|
42
42
|
.boolean()
|