@gajae-code/coding-agent 0.5.4 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +23 -0
- package/dist/types/cli/web-search-cli.d.ts +12 -0
- package/dist/types/commands/rlm.d.ts +10 -0
- package/dist/types/commands/web-search.d.ts +54 -0
- package/dist/types/config/keybindings.d.ts +10 -0
- package/dist/types/config/model-profiles.d.ts +2 -1
- package/dist/types/config/model-registry.d.ts +3 -0
- package/dist/types/config/models-config-schema.d.ts +3 -0
- package/dist/types/config/settings-schema.d.ts +61 -3
- package/dist/types/edit/notebook.d.ts +3 -0
- package/dist/types/eval/py/executor.d.ts +3 -0
- package/dist/types/eval/py/kernel.d.ts +3 -1
- package/dist/types/eval/py/runtime.d.ts +9 -1
- package/dist/types/exec/bash-executor.d.ts +4 -0
- package/dist/types/extensibility/custom-tools/types.d.ts +2 -0
- package/dist/types/extensibility/custom-tools/wrapper.d.ts +1 -0
- package/dist/types/extensibility/extensions/types.d.ts +2 -0
- package/dist/types/extensibility/extensions/wrapper.d.ts +1 -0
- package/dist/types/gjc-runtime/launch-tmux.d.ts +6 -0
- package/dist/types/gjc-runtime/session-state-sidecar.d.ts +14 -0
- package/dist/types/gjc-runtime/tmux-common.d.ts +6 -0
- package/dist/types/gjc-runtime/tmux-gc.d.ts +3 -3
- package/dist/types/gjc-runtime/tmux-sessions.d.ts +4 -0
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +18 -0
- package/dist/types/goals/state.d.ts +1 -1
- package/dist/types/goals/tools/goal-tool.d.ts +2 -0
- package/dist/types/main.d.ts +11 -0
- package/dist/types/modes/components/custom-editor.d.ts +4 -2
- package/dist/types/modes/components/custom-model-preset-wizard.d.ts +12 -0
- package/dist/types/modes/components/model-selector.d.ts +5 -2
- package/dist/types/modes/components/status-line.d.ts +4 -1
- package/dist/types/modes/controllers/input-controller.d.ts +3 -0
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/print-mode.d.ts +6 -0
- package/dist/types/modes/rpc/rpc-client.d.ts +21 -0
- package/dist/types/modes/rpc/rpc-socket-security.d.ts +7 -0
- package/dist/types/modes/rpc/rpc-types.d.ts +13 -0
- package/dist/types/modes/shared/agent-wire/command-dispatch.d.ts +2 -0
- package/dist/types/modes/shared/agent-wire/unattended-session.d.ts +1 -0
- package/dist/types/rlm/artifacts.d.ts +9 -0
- package/dist/types/rlm/complete-research-tool.d.ts +35 -0
- package/dist/types/rlm/data-context.d.ts +6 -0
- package/dist/types/rlm/index.d.ts +35 -0
- package/dist/types/rlm/notebook.d.ts +12 -0
- package/dist/types/rlm/preset.d.ts +23 -0
- package/dist/types/rlm/python-tool.d.ts +16 -0
- package/dist/types/rlm/report.d.ts +14 -0
- package/dist/types/rlm/types.d.ts +37 -0
- package/dist/types/sdk.d.ts +7 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/tools/bash-allowed-prefixes.d.ts +6 -1
- package/dist/types/tools/browser/attach.d.ts +19 -3
- package/dist/types/tools/browser/registry.d.ts +15 -0
- package/dist/types/tools/browser/render.d.ts +3 -0
- package/dist/types/tools/browser.d.ts +18 -1
- package/dist/types/tools/computer/render.d.ts +17 -0
- package/dist/types/tools/computer.d.ts +465 -0
- package/dist/types/tools/index.d.ts +24 -1
- package/dist/types/tools/job.d.ts +13 -0
- package/dist/types/tools/tool-timeouts.d.ts +5 -0
- package/dist/types/web/search/index.d.ts +32 -2
- package/dist/types/web/search/providers/base.d.ts +22 -0
- package/dist/types/web/search/providers/xai.d.ts +64 -0
- package/dist/types/web/search/types.d.ts +11 -3
- package/package.json +7 -7
- package/src/cli/web-search-cli.ts +123 -8
- package/src/cli.ts +2 -0
- package/src/commands/rlm.ts +19 -0
- package/src/commands/web-search.ts +66 -0
- package/src/config/keybindings.ts +11 -0
- package/src/config/model-profiles.ts +11 -3
- package/src/config/model-registry.ts +55 -1
- package/src/config/models-config-schema.ts +1 -0
- package/src/config/settings-schema.ts +67 -1
- package/src/edit/notebook.ts +6 -2
- package/src/eval/py/executor.ts +8 -1
- package/src/eval/py/kernel.ts +9 -4
- package/src/eval/py/runtime.ts +153 -32
- package/src/exec/bash-executor.ts +10 -4
- package/src/extensibility/custom-tools/types.ts +2 -0
- package/src/extensibility/custom-tools/wrapper.ts +2 -0
- package/src/extensibility/extensions/types.ts +2 -0
- package/src/extensibility/extensions/wrapper.ts +1 -0
- package/src/gjc-runtime/launch-tmux.ts +129 -1
- package/src/gjc-runtime/session-state-sidecar.ts +61 -1
- package/src/gjc-runtime/tmux-common.ts +26 -2
- package/src/gjc-runtime/tmux-gc.ts +40 -27
- package/src/gjc-runtime/tmux-sessions.ts +13 -1
- package/src/gjc-runtime/ultragoal-runtime.ts +340 -18
- package/src/goals/runtime.ts +4 -3
- package/src/goals/state.ts +1 -1
- package/src/goals/tools/goal-tool.ts +16 -3
- package/src/internal-urls/docs-index.generated.ts +13 -9
- package/src/main.ts +28 -3
- package/src/modes/components/custom-editor.ts +13 -4
- package/src/modes/components/custom-model-preset-wizard.ts +293 -0
- package/src/modes/components/hook-selector.ts +1 -1
- package/src/modes/components/model-selector.ts +72 -29
- package/src/modes/components/skill-message.ts +62 -8
- package/src/modes/components/status-line.ts +13 -1
- package/src/modes/controllers/input-controller.ts +60 -11
- package/src/modes/controllers/selector-controller.ts +39 -0
- package/src/modes/interactive-mode.ts +1 -1
- package/src/modes/print-mode.ts +14 -4
- package/src/modes/rpc/rpc-client.ts +250 -80
- package/src/modes/rpc/rpc-mode.ts +6 -12
- package/src/modes/rpc/rpc-socket-security.ts +103 -0
- package/src/modes/rpc/rpc-types.ts +10 -0
- package/src/modes/shared/agent-wire/command-dispatch.ts +7 -0
- package/src/modes/shared/agent-wire/command-validation.ts +1 -0
- package/src/modes/shared/agent-wire/scopes.ts +1 -0
- package/src/modes/shared/agent-wire/unattended-session.ts +9 -0
- package/src/modes/utils/hotkeys-markdown.ts +4 -2
- package/src/modes/utils/ui-helpers.ts +2 -2
- package/src/prompts/goals/goal-continuation.md +1 -0
- package/src/prompts/goals/goal-mode-active.md +1 -0
- package/src/prompts/system/rlm-report-command.md +1 -0
- package/src/prompts/system/rlm-research.md +23 -0
- package/src/prompts/tools/bash.md +23 -2
- package/src/prompts/tools/browser.md +7 -3
- package/src/prompts/tools/computer.md +74 -0
- package/src/prompts/tools/goal.md +3 -0
- package/src/prompts/tools/job.md +9 -1
- package/src/prompts/tools/web-search.md +7 -0
- package/src/rlm/artifacts.ts +60 -0
- package/src/rlm/complete-research-tool.ts +163 -0
- package/src/rlm/data-context.ts +26 -0
- package/src/rlm/index.ts +339 -0
- package/src/rlm/notebook.ts +108 -0
- package/src/rlm/preset.ts +76 -0
- package/src/rlm/python-tool.ts +68 -0
- package/src/rlm/report.ts +70 -0
- package/src/rlm/types.ts +40 -0
- package/src/sdk.ts +12 -0
- package/src/session/agent-session.ts +48 -3
- package/src/slash-commands/builtin-registry.ts +17 -0
- package/src/tools/bash-allowed-prefixes.ts +84 -1
- package/src/tools/bash.ts +80 -13
- package/src/tools/browser/attach.ts +103 -3
- package/src/tools/browser/registry.ts +176 -2
- package/src/tools/browser/render.ts +9 -1
- package/src/tools/browser.ts +33 -0
- package/src/tools/computer/render.ts +78 -0
- package/src/tools/computer.ts +640 -0
- package/src/tools/index.ts +41 -1
- package/src/tools/job.ts +88 -5
- package/src/tools/json-tree.ts +42 -29
- package/src/tools/renderers.ts +2 -0
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/web/search/index.ts +27 -2
- package/src/web/search/provider.ts +16 -1
- package/src/web/search/providers/base.ts +22 -0
- package/src/web/search/providers/xai.ts +511 -0
- package/src/web/search/render.ts +7 -0
- package/src/web/search/types.ts +11 -1
package/src/tools/index.ts
CHANGED
|
@@ -33,9 +33,11 @@ import { AskTool } from "./ask";
|
|
|
33
33
|
import { AstEditTool } from "./ast-edit";
|
|
34
34
|
import { AstGrepTool } from "./ast-grep";
|
|
35
35
|
import { BashTool } from "./bash";
|
|
36
|
+
import type { BashRestrictionProfile } from "./bash-allowed-prefixes";
|
|
36
37
|
import { BrowserTool } from "./browser";
|
|
37
38
|
import { CalculatorTool } from "./calculator";
|
|
38
39
|
import { type CheckpointState, CheckpointTool, RewindTool } from "./checkpoint";
|
|
40
|
+
import { ComputerTool, isComputerCallable, isComputerLoadablePlatform } from "./computer";
|
|
39
41
|
import { CronCreateTool, CronDeleteTool, CronListTool } from "./cron";
|
|
40
42
|
import { DebugTool } from "./debug";
|
|
41
43
|
import { EvalTool } from "./eval";
|
|
@@ -73,6 +75,7 @@ export * from "./bash";
|
|
|
73
75
|
export * from "./browser";
|
|
74
76
|
export * from "./calculator";
|
|
75
77
|
export * from "./checkpoint";
|
|
78
|
+
export * from "./computer";
|
|
76
79
|
export * from "./cron";
|
|
77
80
|
export * from "./debug";
|
|
78
81
|
export * from "./eval";
|
|
@@ -159,6 +162,12 @@ export interface ToolSession {
|
|
|
159
162
|
assertEvalExecutionAllowed?: () => void;
|
|
160
163
|
/** Track tool-owned eval work so session disposal can await/abort it like direct session eval runs. */
|
|
161
164
|
trackEvalExecution?<T>(execution: Promise<T>, abortController: AbortController): Promise<T>;
|
|
165
|
+
/** Register a safe request handler that asks a managed foreground bash call to fold into a background job. */
|
|
166
|
+
registerForegroundBashBackgroundRequestHandler?: (handler: () => void) => () => void;
|
|
167
|
+
/** Whether a managed foreground bash call is currently foldable into a background job. */
|
|
168
|
+
hasForegroundBashBackgroundRequestHandler?: () => boolean;
|
|
169
|
+
/** Request that the active managed foreground bash call fold into a background job, if supported. */
|
|
170
|
+
requestForegroundBashBackground?: () => boolean;
|
|
162
171
|
/** Get session ID */
|
|
163
172
|
getSessionId?: () => string | null;
|
|
164
173
|
/** Get Hindsight runtime state for this agent session. */
|
|
@@ -171,8 +180,12 @@ export interface ToolSession {
|
|
|
171
180
|
getToolByName?: (name: string) => AgentTool | undefined;
|
|
172
181
|
/** Agent registry for IRC routing across live sessions. */
|
|
173
182
|
agentRegistry?: AgentRegistry;
|
|
174
|
-
/** Optional restricted bash command prefixes for read-only role agents. */
|
|
183
|
+
/** Optional restricted bash command prefixes for read-only role agents and constrained modes. */
|
|
175
184
|
bashAllowedPrefixes?: string[];
|
|
185
|
+
/** Restriction policy for sessions that deliberately expose a narrow bash surface. */
|
|
186
|
+
bashRestrictionProfile?: BashRestrictionProfile;
|
|
187
|
+
/** Optional per-session allowlist for tools exposed through search_tool_bm25. */
|
|
188
|
+
discoverableToolAllowedNames?: readonly string[];
|
|
176
189
|
/** Get artifacts directory for artifact:// URLs */
|
|
177
190
|
getArtifactsDir?: () => string | null;
|
|
178
191
|
/** Get the ArtifactManager backing this session (shared across parent + subagents). */
|
|
@@ -201,6 +214,8 @@ export interface ToolSession {
|
|
|
201
214
|
getGoalModeState?: () => GoalModeState | undefined;
|
|
202
215
|
/** Unattended workflow-gate emitter (present only when unattended mode is negotiated). */
|
|
203
216
|
getWorkflowGateEmitter?: () => WorkflowGateEmitter | undefined;
|
|
217
|
+
/** Optional per-session restriction for goal tool operations. */
|
|
218
|
+
goalToolAllowedOps?: readonly ("create" | "get" | "complete" | "resume" | "drop" | "pause")[];
|
|
204
219
|
/** Goal runtime for the active agent session. */
|
|
205
220
|
getGoalRuntime?: () => GoalRuntime | undefined;
|
|
206
221
|
/** Bridge to the connected client (e.g. ACP editor host). Tools should route fs/terminal/permission requests through this when available. */
|
|
@@ -312,6 +327,29 @@ export function computeEssentialBuiltinNames(settings: Settings): string[] {
|
|
|
312
327
|
* Hindsight memory helpers are intentionally excluded: memory is a private backend
|
|
313
328
|
* integration, not a public gajae-code tool surface.
|
|
314
329
|
*/
|
|
330
|
+
export interface BuiltinCapabilityCatalogEntry {
|
|
331
|
+
name: string;
|
|
332
|
+
label: string;
|
|
333
|
+
summary: string;
|
|
334
|
+
docsPath: string;
|
|
335
|
+
callableBuiltin: boolean;
|
|
336
|
+
defaultEnabled: boolean;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
export const BUILTIN_CAPABILITY_CATALOG: readonly BuiltinCapabilityCatalogEntry[] = isComputerLoadablePlatform()
|
|
340
|
+
? [
|
|
341
|
+
{
|
|
342
|
+
name: "computer",
|
|
343
|
+
label: "Computer",
|
|
344
|
+
summary:
|
|
345
|
+
"Apple Silicon macOS desktop screenshot and input control; enabled by default on supported hosts and supervisor-gated.",
|
|
346
|
+
docsPath: "docs/tools/computer.md",
|
|
347
|
+
callableBuiltin: false,
|
|
348
|
+
defaultEnabled: true,
|
|
349
|
+
},
|
|
350
|
+
]
|
|
351
|
+
: [];
|
|
352
|
+
|
|
315
353
|
export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
316
354
|
read: s => new ReadTool(s),
|
|
317
355
|
bash: s => new BashTool(s),
|
|
@@ -330,6 +368,7 @@ export const BUILTIN_TOOLS: Record<string, ToolFactory> = {
|
|
|
330
368
|
lsp: LspTool.createIf,
|
|
331
369
|
inspect_image: s => new InspectImageTool(s),
|
|
332
370
|
browser: s => new BrowserTool(s),
|
|
371
|
+
...(isComputerLoadablePlatform() ? { computer: ComputerTool.createIf } : {}),
|
|
333
372
|
checkpoint: CheckpointTool.createIf,
|
|
334
373
|
rewind: RewindTool.createIf,
|
|
335
374
|
task: s => TaskTool.create(s),
|
|
@@ -504,6 +543,7 @@ export async function createTools(session: ToolSession, toolNames?: string[]): P
|
|
|
504
543
|
if (name === "calc") return session.settings.get("calc.enabled");
|
|
505
544
|
if (name === "skill") return session.settings.get("skill.enabled");
|
|
506
545
|
if (name === "browser") return session.settings.get("browser.enabled");
|
|
546
|
+
if (name === "computer") return isComputerCallable(session);
|
|
507
547
|
if (name === "checkpoint" || name === "rewind") return session.settings.get("checkpoint.enabled");
|
|
508
548
|
if (name === "irc") {
|
|
509
549
|
if (!session.settings.get("irc.enabled")) return false;
|
package/src/tools/job.ts
CHANGED
|
@@ -26,6 +26,7 @@ const jobSchema = z.object({
|
|
|
26
26
|
poll: z.array(z.string()).optional().describe("job ids to wait for"),
|
|
27
27
|
cancel: z.array(z.string()).optional().describe("job ids to cancel"),
|
|
28
28
|
list: z.boolean().optional().describe("snapshot all jobs"),
|
|
29
|
+
tail: z.array(z.string()).optional().describe("job ids whose retained output should be shown without waiting"),
|
|
29
30
|
});
|
|
30
31
|
|
|
31
32
|
type JobParams = z.infer<typeof jobSchema>;
|
|
@@ -60,9 +61,19 @@ interface CancelOutcome {
|
|
|
60
61
|
message: string;
|
|
61
62
|
}
|
|
62
63
|
|
|
64
|
+
export interface JobOutputTail {
|
|
65
|
+
id: string;
|
|
66
|
+
status: AsyncJob["status"];
|
|
67
|
+
text: string;
|
|
68
|
+
startOffset: number;
|
|
69
|
+
nextOffset: number;
|
|
70
|
+
truncated: boolean;
|
|
71
|
+
}
|
|
72
|
+
|
|
63
73
|
export interface JobToolDetails {
|
|
64
74
|
jobs: JobSnapshot[];
|
|
65
75
|
cancelled?: { id: string; status: CancelStatus }[];
|
|
76
|
+
output?: JobOutputTail[];
|
|
66
77
|
}
|
|
67
78
|
|
|
68
79
|
export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
@@ -107,7 +118,21 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
107
118
|
if (params.cancel?.length || params.poll?.length) {
|
|
108
119
|
throw new ToolError("`list` cannot be combined with `poll` or `cancel`.");
|
|
109
120
|
}
|
|
110
|
-
return this.#buildResult(
|
|
121
|
+
return this.#buildResult(
|
|
122
|
+
manager,
|
|
123
|
+
manager.getAllJobs(ownerFilter),
|
|
124
|
+
[],
|
|
125
|
+
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (params.tail?.length && !params.poll?.length && !params.cancel?.length) {
|
|
130
|
+
return this.#buildResult(
|
|
131
|
+
manager,
|
|
132
|
+
this.#visibleJobs(manager, params.tail, ownerId),
|
|
133
|
+
[],
|
|
134
|
+
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
135
|
+
);
|
|
111
136
|
}
|
|
112
137
|
|
|
113
138
|
const cancelIds = params.cancel ?? [];
|
|
@@ -150,7 +175,12 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
150
175
|
|
|
151
176
|
if (!shouldPoll) {
|
|
152
177
|
const cancelledJobs = this.#visibleJobs(manager, cancelIds, ownerId);
|
|
153
|
-
return this.#buildResult(
|
|
178
|
+
return this.#buildResult(
|
|
179
|
+
manager,
|
|
180
|
+
cancelledJobs,
|
|
181
|
+
cancelOutcomes,
|
|
182
|
+
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
183
|
+
);
|
|
154
184
|
}
|
|
155
185
|
|
|
156
186
|
// Resolve which jobs to watch.
|
|
@@ -163,7 +193,12 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
163
193
|
if (jobsToWatch.length === 0) {
|
|
164
194
|
if (cancelOutcomes.length > 0) {
|
|
165
195
|
const cancelledJobs = this.#visibleJobs(manager, cancelIds, ownerId);
|
|
166
|
-
return this.#buildResult(
|
|
196
|
+
return this.#buildResult(
|
|
197
|
+
manager,
|
|
198
|
+
cancelledJobs,
|
|
199
|
+
cancelOutcomes,
|
|
200
|
+
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
201
|
+
);
|
|
167
202
|
}
|
|
168
203
|
const message = requestedPollIds?.length
|
|
169
204
|
? `No matching jobs found for IDs: ${requestedPollIds.join(", ")}`
|
|
@@ -178,7 +213,12 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
178
213
|
const runningJobs = jobsToWatch.filter(j => j.status === "running");
|
|
179
214
|
if (runningJobs.length === 0) {
|
|
180
215
|
const cancelledJobs = cancelIds.map(id => manager.getJob(id)).filter(j => j != null);
|
|
181
|
-
return this.#buildResult(
|
|
216
|
+
return this.#buildResult(
|
|
217
|
+
manager,
|
|
218
|
+
[...cancelledJobs, ...jobsToWatch],
|
|
219
|
+
cancelOutcomes,
|
|
220
|
+
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
221
|
+
);
|
|
182
222
|
}
|
|
183
223
|
|
|
184
224
|
// Wait until at least one running job finishes, the wait duration elapses, or the call is aborted.
|
|
@@ -231,7 +271,12 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
231
271
|
if (progressTimer) clearInterval(progressTimer);
|
|
232
272
|
}
|
|
233
273
|
|
|
234
|
-
return this.#buildResult(
|
|
274
|
+
return this.#buildResult(
|
|
275
|
+
manager,
|
|
276
|
+
allTrackedJobs,
|
|
277
|
+
cancelOutcomes,
|
|
278
|
+
this.#readOutputTails(manager, params.tail, ownerFilter),
|
|
279
|
+
);
|
|
235
280
|
}
|
|
236
281
|
|
|
237
282
|
/**
|
|
@@ -239,6 +284,28 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
239
284
|
* Drops missing ids and ids owned by other agents, so cross-agent inspection
|
|
240
285
|
* via the `job` tool is impossible.
|
|
241
286
|
*/
|
|
287
|
+
#readOutputTails(
|
|
288
|
+
manager: AsyncJobManager,
|
|
289
|
+
ids: string[] | undefined,
|
|
290
|
+
ownerFilter: { ownerId: string } | undefined,
|
|
291
|
+
): JobOutputTail[] {
|
|
292
|
+
if (!ids?.length) return [];
|
|
293
|
+
const out: JobOutputTail[] = [];
|
|
294
|
+
for (const id of Array.from(new Set(ids.map(value => value.trim()).filter(Boolean)))) {
|
|
295
|
+
const slice = manager.readOutputSince(id, 0, ownerFilter);
|
|
296
|
+
if (!slice) continue;
|
|
297
|
+
out.push({
|
|
298
|
+
id: slice.jobId,
|
|
299
|
+
status: slice.status,
|
|
300
|
+
text: slice.text,
|
|
301
|
+
startOffset: slice.startOffset,
|
|
302
|
+
nextOffset: slice.nextOffset,
|
|
303
|
+
truncated: slice.truncated,
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
|
|
242
309
|
#visibleJobs(manager: AsyncJobManager, ids: string[], ownerId: string | undefined): AsyncJob[] {
|
|
243
310
|
const out: AsyncJob[] = [];
|
|
244
311
|
for (const id of ids) {
|
|
@@ -290,6 +357,7 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
290
357
|
errorText?: string;
|
|
291
358
|
}[],
|
|
292
359
|
cancelOutcomes: CancelOutcome[],
|
|
360
|
+
outputTails: JobOutputTail[] = [],
|
|
293
361
|
): AgentToolResult<JobToolDetails> {
|
|
294
362
|
// Deduplicate by id (cancelled jobs may also appear in the watched set).
|
|
295
363
|
const seen = new Set<string>();
|
|
@@ -335,11 +403,21 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
335
403
|
}
|
|
336
404
|
}
|
|
337
405
|
|
|
406
|
+
if (outputTails.length > 0) {
|
|
407
|
+
lines.push("", `## Retained Output (${outputTails.length})\n`);
|
|
408
|
+
for (const tail of outputTails) {
|
|
409
|
+
lines.push(`### ${tail.id} — ${tail.status}`);
|
|
410
|
+
if (tail.truncated) lines.push(`(showing retained tail from byte ${tail.startOffset})`);
|
|
411
|
+
lines.push("```", tail.text || "(no retained output yet)", "```", `cursor: ${tail.nextOffset}`, "");
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
|
|
338
415
|
return {
|
|
339
416
|
content: [{ type: "text", text: lines.join("\n").trimEnd() }],
|
|
340
417
|
details: {
|
|
341
418
|
jobs: jobResults,
|
|
342
419
|
...(cancelOutcomes.length ? { cancelled: cancelOutcomes.map(({ id, status }) => ({ id, status })) } : {}),
|
|
420
|
+
...(outputTails.length ? { output: outputTails } : {}),
|
|
343
421
|
},
|
|
344
422
|
};
|
|
345
423
|
}
|
|
@@ -352,6 +430,7 @@ export class JobTool implements AgentTool<typeof jobSchema, JobToolDetails> {
|
|
|
352
430
|
interface JobRenderArgs {
|
|
353
431
|
poll?: string[];
|
|
354
432
|
cancel?: string[];
|
|
433
|
+
tail?: string[];
|
|
355
434
|
}
|
|
356
435
|
|
|
357
436
|
const COLLAPSED_LIST_LIMIT = PREVIEW_LIMITS.COLLAPSED_ITEMS;
|
|
@@ -391,6 +470,7 @@ function statusToColor(status: JobSnapshot["status"]): ToolUIColor {
|
|
|
391
470
|
function describeTarget(args: JobRenderArgs | undefined): string {
|
|
392
471
|
const poll = args?.poll ?? [];
|
|
393
472
|
const cancel = args?.cancel ?? [];
|
|
473
|
+
const tail = args?.tail ?? [];
|
|
394
474
|
const parts: string[] = [];
|
|
395
475
|
if (cancel.length > 0) {
|
|
396
476
|
parts.push(cancel.length === 1 ? `cancel ${cancel[0]}` : `cancel ${cancel.length} jobs`);
|
|
@@ -398,6 +478,9 @@ function describeTarget(args: JobRenderArgs | undefined): string {
|
|
|
398
478
|
if (poll.length > 0) {
|
|
399
479
|
parts.push(poll.length === 1 ? `poll ${poll[0]}` : `poll ${poll.length} jobs`);
|
|
400
480
|
}
|
|
481
|
+
if (tail.length > 0) {
|
|
482
|
+
parts.push(tail.length === 1 ? `tail ${tail[0]}` : `tail ${tail.length} jobs`);
|
|
483
|
+
}
|
|
401
484
|
if (parts.length === 0) return "all running jobs";
|
|
402
485
|
return parts.join(", ");
|
|
403
486
|
}
|
package/src/tools/json-tree.ts
CHANGED
|
@@ -81,6 +81,25 @@ function buildTreePrefix(theme: Theme, ancestors: readonly boolean[]): string {
|
|
|
81
81
|
return ancestors.map(hasNext => (hasNext ? `${theme.tree.vertical} ` : " ")).join("");
|
|
82
82
|
}
|
|
83
83
|
|
|
84
|
+
function splitByDisplayWidth(value: string, maxWidth: number): string[] {
|
|
85
|
+
const width = Math.max(1, maxWidth);
|
|
86
|
+
const fragments: string[] = [];
|
|
87
|
+
let current = "";
|
|
88
|
+
let currentWidth = 0;
|
|
89
|
+
for (const char of value) {
|
|
90
|
+
const charWidth = Math.max(1, Bun.stringWidth(char));
|
|
91
|
+
if (current && currentWidth + charWidth > width) {
|
|
92
|
+
fragments.push(current);
|
|
93
|
+
current = "";
|
|
94
|
+
currentWidth = 0;
|
|
95
|
+
}
|
|
96
|
+
current += char;
|
|
97
|
+
currentWidth += charWidth;
|
|
98
|
+
}
|
|
99
|
+
if (current || fragments.length === 0) fragments.push(current);
|
|
100
|
+
return fragments;
|
|
101
|
+
}
|
|
102
|
+
|
|
84
103
|
/**
|
|
85
104
|
* Render a JSON value as tree lines.
|
|
86
105
|
*/
|
|
@@ -121,38 +140,32 @@ export function renderJsonTreeLines(
|
|
|
121
140
|
// Handle scalars
|
|
122
141
|
if (val === null || val === undefined || typeof val !== "object") {
|
|
123
142
|
const label = key ? theme.fg("muted", key) : theme.fg("muted", "value");
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
const
|
|
128
|
-
const
|
|
143
|
+
if (typeof val === "string") {
|
|
144
|
+
const escaped = val.replace(/\n/g, "\\n").replace(/\t/g, "\\t");
|
|
145
|
+
const marker = "…";
|
|
146
|
+
const firstFragmentWidth = Math.max(1, maxScalarLen - 2);
|
|
147
|
+
const continuationWidth = Math.max(1, maxScalarLen - Bun.stringWidth(marker) - 1);
|
|
148
|
+
const firstFragments = splitByDisplayWidth(escaped, firstFragmentWidth);
|
|
149
|
+
const firstFragment = firstFragments[0] ?? "";
|
|
150
|
+
const remainder = firstFragments.slice(1).join("");
|
|
151
|
+
const continuationFragments = remainder ? splitByDisplayWidth(remainder, continuationWidth) : [];
|
|
152
|
+
const fragments = [firstFragment, ...continuationFragments];
|
|
153
|
+
const lineBudget = Math.max(1, maxLines - lines.length);
|
|
154
|
+
const visibleCount = Math.min(fragments.length, lineBudget);
|
|
129
155
|
const continuePrefix = buildTreePrefix(theme, ancestors);
|
|
156
|
+
const isStringTruncated = visibleCount < fragments.length;
|
|
130
157
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
141
|
-
const line = truncateToWidth(strLines[i], maxScalarLen);
|
|
142
|
-
pushLine(`${continuePrefix} ${theme.fg("dim", ` ${line}`)}`);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Show truncation and closing quote
|
|
146
|
-
if (strLines.length > maxStrLines) {
|
|
147
|
-
truncated = true;
|
|
148
|
-
pushLine(
|
|
149
|
-
`${continuePrefix} ${theme.fg("dim", ` …(${strLines.length - maxStrLines} more lines)"`)}`,
|
|
150
|
-
);
|
|
151
|
-
} else {
|
|
152
|
-
// Add closing quote to last line - need to modify the last pushed line
|
|
153
|
-
const lastIdx = lines.length - 1;
|
|
154
|
-
lines[lastIdx] = `${lines[lastIdx]}${theme.fg("dim", '"')}`;
|
|
158
|
+
for (let i = 0; i < visibleCount; i++) {
|
|
159
|
+
const isFirst = i === 0;
|
|
160
|
+
const isFinalVisible = i === visibleCount - 1;
|
|
161
|
+
const suffix = isStringTruncated && isFinalVisible ? `${marker}"` : isFinalVisible ? '"' : "";
|
|
162
|
+
const rendered = isFirst ? `"${fragments[i]}${suffix}` : `↳ ${fragments[i]}${suffix}`;
|
|
163
|
+
const line = isFirst
|
|
164
|
+
? `${prefix}${iconScalar} ${label}: ${theme.fg("dim", rendered)}`
|
|
165
|
+
: `${continuePrefix} ${theme.fg("dim", rendered)}`;
|
|
166
|
+
if (!pushLine(line)) break;
|
|
155
167
|
}
|
|
168
|
+
if (isStringTruncated) truncated = true;
|
|
156
169
|
return;
|
|
157
170
|
}
|
|
158
171
|
|
package/src/tools/renderers.ts
CHANGED
|
@@ -17,6 +17,7 @@ import { astGrepToolRenderer } from "./ast-grep";
|
|
|
17
17
|
import { bashToolRenderer } from "./bash";
|
|
18
18
|
import { browserToolRenderer } from "./browser/render";
|
|
19
19
|
import { calculatorToolRenderer } from "./calculator";
|
|
20
|
+
import { computerToolRenderer } from "./computer/render";
|
|
20
21
|
import { debugToolRenderer } from "./debug";
|
|
21
22
|
import { evalToolRenderer } from "./eval";
|
|
22
23
|
import { findToolRenderer } from "./find";
|
|
@@ -52,6 +53,7 @@ export const toolRenderers: Record<string, ToolRenderer> = {
|
|
|
52
53
|
ast_edit: astEditToolRenderer as ToolRenderer,
|
|
53
54
|
bash: bashToolRenderer as ToolRenderer,
|
|
54
55
|
browser: browserToolRenderer as ToolRenderer,
|
|
56
|
+
computer: computerToolRenderer as ToolRenderer,
|
|
55
57
|
recipe: recipeToolRenderer as ToolRenderer,
|
|
56
58
|
debug: debugToolRenderer as ToolRenderer,
|
|
57
59
|
eval: evalToolRenderer as ToolRenderer,
|
|
@@ -11,6 +11,7 @@ export const TOOL_TIMEOUTS = {
|
|
|
11
11
|
bash: { default: 300, min: 1, max: 3600 },
|
|
12
12
|
eval: { default: 30, min: 1, max: 600 },
|
|
13
13
|
browser: { default: 30, min: 1, max: 300 },
|
|
14
|
+
computer: { default: 30, min: 1, max: 300 },
|
|
14
15
|
ssh: { default: 60, min: 1, max: 3600 },
|
|
15
16
|
fetch: { default: 20, min: 1, max: 45 },
|
|
16
17
|
lsp: { default: 20, min: 5, max: 60 },
|
package/src/web/search/index.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Unified Web Search Tool
|
|
3
3
|
*
|
|
4
|
-
* Single tool supporting Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, OpenAI code backend, Tavily, Kagi, Z.AI, SearXNG, and Synthetic
|
|
4
|
+
* Single tool supporting Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, OpenAI code backend, xAI, Tavily, Kagi, Z.AI, SearXNG, and Synthetic
|
|
5
5
|
* providers with provider-specific parameters exposed conditionally.
|
|
6
6
|
*/
|
|
7
7
|
import type { AgentTool, AgentToolContext, AgentToolResult, AgentToolUpdateCallback } from "@gajae-code/agent-core";
|
|
@@ -29,6 +29,20 @@ export const webSearchSchema = z.object({
|
|
|
29
29
|
max_tokens: z.number().describe("max output tokens").optional(),
|
|
30
30
|
temperature: z.number().describe("sampling temperature").optional(),
|
|
31
31
|
num_search_results: z.number().describe("number of search results").optional(),
|
|
32
|
+
xai_search_mode: z
|
|
33
|
+
.enum(["web", "x", "web_and_x"])
|
|
34
|
+
.describe("xAI only: use web_search, x_search, or both")
|
|
35
|
+
.optional(),
|
|
36
|
+
allowed_domains: z.array(z.string()).max(5).describe("xAI web_search only: allowed domains").optional(),
|
|
37
|
+
excluded_domains: z.array(z.string()).max(5).describe("xAI web_search only: excluded domains").optional(),
|
|
38
|
+
allowed_x_handles: z.array(z.string()).max(20).describe("xAI x_search only: allowed X handles").optional(),
|
|
39
|
+
excluded_x_handles: z.array(z.string()).max(20).describe("xAI x_search only: excluded X handles").optional(),
|
|
40
|
+
from_date: z.string().describe("xAI x_search only: start date in ISO8601 format").optional(),
|
|
41
|
+
to_date: z.string().describe("xAI x_search only: end date in ISO8601 format").optional(),
|
|
42
|
+
enable_image_understanding: z.boolean().describe("xAI only: analyze images encountered during search").optional(),
|
|
43
|
+
enable_image_search: z.boolean().describe("xAI web_search only: search for and embed image results").optional(),
|
|
44
|
+
enable_video_understanding: z.boolean().describe("xAI x_search only: analyze videos in X posts").optional(),
|
|
45
|
+
no_inline_citations: z.boolean().describe("xAI only: disable inline citation markdown in the answer").optional(),
|
|
32
46
|
});
|
|
33
47
|
|
|
34
48
|
export type SearchToolParams = z.infer<typeof webSearchSchema>;
|
|
@@ -153,6 +167,17 @@ async function executeSearch(
|
|
|
153
167
|
maxOutputTokens: params.max_tokens,
|
|
154
168
|
numSearchResults: params.num_search_results,
|
|
155
169
|
temperature: params.temperature,
|
|
170
|
+
xaiSearchMode: params.xai_search_mode,
|
|
171
|
+
allowedDomains: params.allowed_domains,
|
|
172
|
+
excludedDomains: params.excluded_domains,
|
|
173
|
+
allowedXHandles: params.allowed_x_handles,
|
|
174
|
+
excludedXHandles: params.excluded_x_handles,
|
|
175
|
+
fromDate: params.from_date,
|
|
176
|
+
toDate: params.to_date,
|
|
177
|
+
enableImageUnderstanding: params.enable_image_understanding,
|
|
178
|
+
enableImageSearch: params.enable_image_search,
|
|
179
|
+
enableVideoUnderstanding: params.enable_video_understanding,
|
|
180
|
+
noInlineCitations: params.no_inline_citations,
|
|
156
181
|
signal,
|
|
157
182
|
authStorage,
|
|
158
183
|
sessionId,
|
|
@@ -225,7 +250,7 @@ export async function runSearchQuery(
|
|
|
225
250
|
/**
|
|
226
251
|
* Web search tool implementation.
|
|
227
252
|
*
|
|
228
|
-
* Supports Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, OpenAI code backend, Z.AI, SearXNG, and Synthetic providers with automatic fallback.
|
|
253
|
+
* Supports Anthropic, Perplexity, Exa, Brave, Jina, Kimi, Gemini, OpenAI code backend, xAI, Z.AI, SearXNG, and Synthetic providers with automatic fallback.
|
|
229
254
|
*/
|
|
230
255
|
export class WebSearchTool implements AgentTool<typeof webSearchSchema, SearchRenderDetails> {
|
|
231
256
|
readonly name = "web_search";
|
|
@@ -45,6 +45,7 @@ const PROVIDER_META: Record<SearchProviderId, ProviderMeta> = {
|
|
|
45
45
|
load: async () => new (await import("./providers/gemini")).GeminiProvider(),
|
|
46
46
|
},
|
|
47
47
|
codex: { id: "codex", label: "OpenAI", load: async () => new (await import("./providers/codex")).CodexProvider() },
|
|
48
|
+
xai: { id: "xai", label: "xAI", load: async () => new (await import("./providers/xai")).XaiProvider() },
|
|
48
49
|
tavily: {
|
|
49
50
|
id: "tavily",
|
|
50
51
|
label: "Tavily",
|
|
@@ -104,6 +105,7 @@ export const SEARCH_PROVIDER_ORDER: SearchProviderId[] = [
|
|
|
104
105
|
"anthropic",
|
|
105
106
|
"gemini",
|
|
106
107
|
"codex",
|
|
108
|
+
"xai",
|
|
107
109
|
"zai",
|
|
108
110
|
"exa",
|
|
109
111
|
"parallel",
|
|
@@ -116,6 +118,7 @@ const MODEL_PROVIDER_TO_SEARCH: Record<string, SearchProviderId> = {
|
|
|
116
118
|
openai: "codex",
|
|
117
119
|
"openai-codex": "codex",
|
|
118
120
|
"openai-responses": "codex",
|
|
121
|
+
xai: "xai",
|
|
119
122
|
anthropic: "anthropic",
|
|
120
123
|
google: "gemini",
|
|
121
124
|
"google-gemini-cli": "gemini",
|
|
@@ -185,6 +188,16 @@ function looksOpenAIFamilyModelId(ctx: ActiveSearchModelContext): boolean {
|
|
|
185
188
|
return looksHostedModelId(ctx.wireModelId) || looksHostedModelId(ctx.modelId);
|
|
186
189
|
}
|
|
187
190
|
|
|
191
|
+
function looksXaiModelId(modelId: string | undefined): boolean {
|
|
192
|
+
if (!modelId) return false;
|
|
193
|
+
const id = modelId.toLowerCase();
|
|
194
|
+
return id.startsWith("grok-") || id.startsWith("x-ai/grok-") || id.startsWith("xai/grok-");
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function looksXaiFamilyModelId(ctx: ActiveSearchModelContext): boolean {
|
|
198
|
+
return looksXaiModelId(ctx.wireModelId) || looksXaiModelId(ctx.modelId);
|
|
199
|
+
}
|
|
200
|
+
|
|
188
201
|
export function isLocalBaseUrl(baseUrl: string | undefined): boolean {
|
|
189
202
|
if (!baseUrl) return false;
|
|
190
203
|
let url: URL;
|
|
@@ -226,6 +239,7 @@ export function inferNativeProviderFromModel(ctx: ActiveSearchModelContext | und
|
|
|
226
239
|
const modelId = (ctx.wireModelId ?? ctx.modelId).toLowerCase();
|
|
227
240
|
if (modelId.startsWith("claude-") && isAnthropicWire(ctx.api)) return "anthropic";
|
|
228
241
|
if (modelId.startsWith("gemini-") && isGoogleWire(ctx.api)) return "gemini";
|
|
242
|
+
if (looksXaiFamilyModelId(ctx) && isOpenAICompatWire(ctx.api)) return "xai";
|
|
229
243
|
if (looksOpenAIFamilyModelId(ctx) && isOpenAICompatWire(ctx.api)) {
|
|
230
244
|
if (ctx.webSearch === "on" || !isLocalBaseUrl(ctx.baseUrl)) return "codex";
|
|
231
245
|
}
|
|
@@ -290,7 +304,8 @@ export async function resolveProviderChain(options: ResolveProviderChainOptions)
|
|
|
290
304
|
await appendAvailable(chain, directId, authStorage);
|
|
291
305
|
const inferred = inferNativeProviderFromModel(activeModelContext);
|
|
292
306
|
if (inferred) await appendAvailable(chain, inferred, authStorage);
|
|
293
|
-
|
|
307
|
+
const hasNativeXai = chain.includes("xai");
|
|
308
|
+
if (!hasNativeXai && (await shouldTryGenericOpenAICompat(authStorage, activeModelContext, sessionId, signal)))
|
|
294
309
|
appendDeduped(chain, "openai-compatible");
|
|
295
310
|
}
|
|
296
311
|
|
|
@@ -33,6 +33,28 @@ export interface SearchParams {
|
|
|
33
33
|
maxOutputTokens?: number;
|
|
34
34
|
numSearchResults?: number;
|
|
35
35
|
temperature?: number;
|
|
36
|
+
/** xAI-specific search surface. Defaults to web_search when omitted. */
|
|
37
|
+
xaiSearchMode?: "web" | "x" | "web_and_x";
|
|
38
|
+
/** xAI web_search domain allow-list (max 5). */
|
|
39
|
+
allowedDomains?: string[];
|
|
40
|
+
/** xAI web_search domain deny-list (max 5). */
|
|
41
|
+
excludedDomains?: string[];
|
|
42
|
+
/** xAI x_search handle allow-list (max 20). */
|
|
43
|
+
allowedXHandles?: string[];
|
|
44
|
+
/** xAI x_search handle deny-list (max 20). */
|
|
45
|
+
excludedXHandles?: string[];
|
|
46
|
+
/** xAI x_search lower date bound, ISO8601 date such as YYYY-MM-DD. */
|
|
47
|
+
fromDate?: string;
|
|
48
|
+
/** xAI x_search upper date bound, ISO8601 date such as YYYY-MM-DD. */
|
|
49
|
+
toDate?: string;
|
|
50
|
+
/** xAI web_search/x_search image understanding. */
|
|
51
|
+
enableImageUnderstanding?: boolean;
|
|
52
|
+
/** xAI web_search image search result embedding. */
|
|
53
|
+
enableImageSearch?: boolean;
|
|
54
|
+
/** xAI x_search video understanding. */
|
|
55
|
+
enableVideoUnderstanding?: boolean;
|
|
56
|
+
/** xAI Responses include=["no_inline_citations"]. */
|
|
57
|
+
noInlineCitations?: boolean;
|
|
36
58
|
googleSearch?: Record<string, unknown>;
|
|
37
59
|
codeExecution?: Record<string, unknown>;
|
|
38
60
|
urlContext?: Record<string, unknown>;
|