@gajae-code/coding-agent 0.6.4 → 0.6.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 +22 -0
- package/dist/types/cli/migrate-cli.d.ts +20 -0
- package/dist/types/commands/migrate.d.ts +33 -0
- package/dist/types/config/keybindings.d.ts +4 -0
- package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +2 -2
- package/dist/types/gjc-runtime/goal-mode-request.d.ts +1 -1
- package/dist/types/gjc-runtime/session-layout.d.ts +59 -0
- package/dist/types/gjc-runtime/session-resolution.d.ts +47 -0
- package/dist/types/gjc-runtime/state-graph.d.ts +1 -1
- package/dist/types/gjc-runtime/state-runtime.d.ts +5 -4
- package/dist/types/gjc-runtime/state-schema.d.ts +2 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +36 -7
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +7 -4
- package/dist/types/gjc-runtime/workflow-command-ref.d.ts +1 -1
- package/dist/types/gjc-runtime/workflow-manifest.d.ts +1 -1
- package/dist/types/harness-control-plane/storage.d.ts +2 -1
- package/dist/types/hooks/skill-state.d.ts +12 -4
- package/dist/types/migrate/action-planner.d.ts +11 -0
- package/dist/types/migrate/adapters/claude-code.d.ts +2 -0
- package/dist/types/migrate/adapters/codex.d.ts +5 -0
- package/dist/types/migrate/adapters/index.d.ts +45 -0
- package/dist/types/migrate/adapters/opencode.d.ts +2 -0
- package/dist/types/migrate/executor.d.ts +2 -0
- package/dist/types/migrate/mcp-mapper.d.ts +20 -0
- package/dist/types/migrate/report.d.ts +18 -0
- package/dist/types/migrate/skill-normalizer.d.ts +27 -0
- package/dist/types/migrate/types.d.ts +126 -0
- package/dist/types/modes/components/custom-editor.d.ts +1 -1
- package/dist/types/modes/shared/agent-wire/unattended-audit.d.ts +1 -1
- package/dist/types/research-plan/index.d.ts +1 -0
- package/dist/types/research-plan/ledger.d.ts +33 -0
- package/dist/types/rlm/artifacts.d.ts +1 -1
- package/dist/types/runtime-mcp/config-writer.d.ts +26 -0
- package/dist/types/skill-state/active-state.d.ts +6 -11
- package/dist/types/skill-state/canonical-skills.d.ts +3 -0
- package/dist/types/skill-state/workflow-hud.d.ts +2 -0
- package/dist/types/task/spawn-gate.d.ts +1 -10
- package/package.json +7 -7
- package/src/cli/migrate-cli.ts +106 -0
- package/src/cli.ts +1 -0
- package/src/commands/deep-interview.ts +2 -2
- package/src/commands/migrate.ts +46 -0
- package/src/commands/state.ts +2 -1
- package/src/commands/team.ts +7 -3
- package/src/coordinator-mcp/policy.ts +10 -2
- package/src/defaults/gjc/extensions/grok-cli-vendor/biome.json +0 -1
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +28 -24
- package/src/defaults/gjc/skills/ralplan/SKILL.md +8 -4
- package/src/defaults/gjc/skills/team/SKILL.md +51 -47
- package/src/defaults/gjc/skills/ultragoal/SKILL.md +17 -13
- package/src/extensibility/custom-commands/loader.ts +0 -7
- package/src/extensibility/gjc-plugins/injection.ts +23 -4
- package/src/extensibility/gjc-plugins/state.ts +16 -1
- package/src/gjc-runtime/deep-interview-recorder.ts +43 -18
- package/src/gjc-runtime/deep-interview-runtime.ts +49 -23
- package/src/gjc-runtime/goal-mode-request.ts +26 -11
- package/src/gjc-runtime/launch-tmux.ts +6 -1
- package/src/gjc-runtime/ralplan-runtime.ts +79 -50
- package/src/gjc-runtime/session-layout.ts +180 -0
- package/src/gjc-runtime/session-resolution.ts +217 -0
- package/src/gjc-runtime/state-graph.ts +1 -2
- package/src/gjc-runtime/state-migrations.ts +1 -0
- package/src/gjc-runtime/state-runtime.ts +230 -121
- package/src/gjc-runtime/state-schema.ts +2 -0
- package/src/gjc-runtime/state-writer.ts +289 -41
- package/src/gjc-runtime/team-runtime.ts +43 -19
- package/src/gjc-runtime/tmux-sessions.ts +7 -1
- package/src/gjc-runtime/ultragoal-guard.ts +45 -2
- package/src/gjc-runtime/ultragoal-runtime.ts +121 -41
- package/src/gjc-runtime/workflow-command-ref.ts +1 -2
- package/src/gjc-runtime/workflow-manifest.ts +1 -2
- package/src/harness-control-plane/storage.ts +14 -4
- package/src/hooks/native-skill-hook.ts +38 -12
- package/src/hooks/skill-state.ts +178 -83
- package/src/internal-urls/docs-index.generated.ts +6 -4
- package/src/migrate/action-planner.ts +318 -0
- package/src/migrate/adapters/claude-code.ts +39 -0
- package/src/migrate/adapters/codex.ts +70 -0
- package/src/migrate/adapters/index.ts +277 -0
- package/src/migrate/adapters/opencode.ts +52 -0
- package/src/migrate/executor.ts +81 -0
- package/src/migrate/mcp-mapper.ts +152 -0
- package/src/migrate/report.ts +104 -0
- package/src/migrate/skill-normalizer.ts +80 -0
- package/src/migrate/types.ts +163 -0
- package/src/modes/bridge/bridge-mode.ts +2 -2
- package/src/modes/components/custom-editor.ts +30 -20
- package/src/modes/rpc/rpc-mode.ts +2 -2
- package/src/modes/shared/agent-wire/unattended-audit.ts +3 -2
- package/src/prompts/agents/init.md +1 -1
- package/src/prompts/system/plan-mode-active.md +1 -1
- package/src/prompts/tools/ast-grep.md +1 -1
- package/src/prompts/tools/search.md +1 -1
- package/src/prompts/tools/task.md +1 -2
- package/src/research-plan/index.ts +1 -0
- package/src/research-plan/ledger.ts +177 -0
- package/src/rlm/artifacts.ts +12 -3
- package/src/rlm/index.ts +7 -0
- package/src/runtime-mcp/config-writer.ts +46 -0
- package/src/session/agent-session.ts +15 -21
- package/src/setup/hermes-setup.ts +1 -1
- package/src/skill-state/active-state.ts +72 -108
- package/src/skill-state/canonical-skills.ts +4 -0
- package/src/skill-state/deep-interview-mutation-guard.ts +28 -109
- package/src/skill-state/workflow-hud.ts +4 -2
- package/src/skill-state/workflow-state-contract.ts +3 -3
- package/src/task/agents.ts +1 -22
- package/src/task/index.ts +1 -41
- package/src/task/spawn-gate.ts +1 -38
- package/src/task/types.ts +1 -1
- package/src/tools/ask.ts +34 -12
- package/src/tools/computer.ts +58 -4
- package/dist/types/extensibility/custom-commands/bundled/review/index.d.ts +0 -10
- package/src/extensibility/custom-commands/bundled/review/index.ts +0 -456
- package/src/prompts/agents/explore.md +0 -58
- package/src/prompts/agents/plan.md +0 -49
- package/src/prompts/agents/reviewer.md +0 -141
- package/src/prompts/agents/task.md +0 -16
- package/src/prompts/review-request.md +0 -70
package/src/task/index.ts
CHANGED
|
@@ -55,7 +55,7 @@ import { assertNoRawTaskFields, buildTaskReceipt, buildTaskRoiSummary } from "./
|
|
|
55
55
|
import { renderResult, renderCall as renderTaskCall } from "./render";
|
|
56
56
|
import { reconcileSpawnRoi } from "./roi-reconciliation";
|
|
57
57
|
import { getTaskSimpleModeCapabilities, type TaskSimpleMode } from "./simple-mode";
|
|
58
|
-
import { DEFAULT_SPAWN_THRESHOLD,
|
|
58
|
+
import { DEFAULT_SPAWN_THRESHOLD, evaluateSpawnGate } from "./spawn-gate";
|
|
59
59
|
import {
|
|
60
60
|
applyNestedPatches,
|
|
61
61
|
captureBaseline,
|
|
@@ -359,7 +359,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
359
359
|
readonly renderResult = renderResult;
|
|
360
360
|
readonly #discoveredAgents: AgentDefinition[];
|
|
361
361
|
readonly #blockedAgent: string | undefined;
|
|
362
|
-
readonly #spawningAgentType: string | undefined;
|
|
363
362
|
|
|
364
363
|
get parameters(): TaskToolSchemaInstance {
|
|
365
364
|
const isolationEnabled = this.session.settings.get("task.isolation.mode") !== "none";
|
|
@@ -391,7 +390,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
391
390
|
discoveredAgents: AgentDefinition[],
|
|
392
391
|
) {
|
|
393
392
|
this.#blockedAgent = $env.PI_BLOCKED_AGENT;
|
|
394
|
-
this.#spawningAgentType = session.currentAgentType;
|
|
395
393
|
this.#discoveredAgents = discoveredAgents;
|
|
396
394
|
}
|
|
397
395
|
|
|
@@ -478,23 +476,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
478
476
|
};
|
|
479
477
|
}
|
|
480
478
|
|
|
481
|
-
const reviewerExploreDecision = evaluateReviewerExploreGate({
|
|
482
|
-
spawningAgentType: this.#spawningAgentType,
|
|
483
|
-
targetAgent: params.agent,
|
|
484
|
-
plan: params.spawnPlan,
|
|
485
|
-
});
|
|
486
|
-
if (reviewerExploreDecision.outcome === "rejected") {
|
|
487
|
-
return {
|
|
488
|
-
content: [
|
|
489
|
-
{
|
|
490
|
-
type: "text",
|
|
491
|
-
text: `Task spawn gate rejected reviewer->explore: ${reviewerExploreDecision.reason}. Provide spawnPlan fields: ${reviewerExploreDecision.missingFields.join(", ")}.`,
|
|
492
|
-
},
|
|
493
|
-
],
|
|
494
|
-
details: { projectAgentsDir: null, results: [], totalDurationMs: 0 },
|
|
495
|
-
};
|
|
496
|
-
}
|
|
497
|
-
|
|
498
479
|
const manager = AsyncJobManager.instance();
|
|
499
480
|
if (!manager) {
|
|
500
481
|
return {
|
|
@@ -1083,27 +1064,6 @@ export class TaskTool implements AgentTool<TaskToolSchemaInstance, TaskToolDetai
|
|
|
1083
1064
|
};
|
|
1084
1065
|
}
|
|
1085
1066
|
|
|
1086
|
-
const reviewerExploreDecision = evaluateReviewerExploreGate({
|
|
1087
|
-
spawningAgentType: this.#spawningAgentType,
|
|
1088
|
-
targetAgent: agentName,
|
|
1089
|
-
plan: params.spawnPlan,
|
|
1090
|
-
});
|
|
1091
|
-
if (reviewerExploreDecision.outcome === "rejected") {
|
|
1092
|
-
return {
|
|
1093
|
-
content: [
|
|
1094
|
-
{
|
|
1095
|
-
type: "text",
|
|
1096
|
-
text: `Task spawn gate rejected reviewer->explore: ${reviewerExploreDecision.reason}. Provide spawnPlan fields: ${reviewerExploreDecision.missingFields.join(", ")}.`,
|
|
1097
|
-
},
|
|
1098
|
-
],
|
|
1099
|
-
details: {
|
|
1100
|
-
projectAgentsDir,
|
|
1101
|
-
results: [],
|
|
1102
|
-
totalDurationMs: Date.now() - startTime,
|
|
1103
|
-
},
|
|
1104
|
-
};
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
1067
|
let repoRoot: string | null = null;
|
|
1108
1068
|
let baseline: WorktreeBaseline | null = null;
|
|
1109
1069
|
if (isIsolated) {
|
package/src/task/spawn-gate.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/** The hard, locked batch threshold enforced by the runtime gate. */
|
|
2
2
|
export const DEFAULT_SPAWN_THRESHOLD = 4;
|
|
3
3
|
|
|
4
|
-
/** The justification a large batch
|
|
4
|
+
/** The justification a large batch must supply to pass the hard gate. */
|
|
5
5
|
export interface SpawnPlanReceipt {
|
|
6
6
|
whyParallel: string;
|
|
7
7
|
whyNotLocal: string;
|
|
@@ -17,15 +17,6 @@ export interface SpawnGateRequest {
|
|
|
17
17
|
plan?: SpawnPlanReceipt;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export interface ReviewerExploreGateRequest {
|
|
21
|
-
/** Agent type/name doing the spawning, when known. */
|
|
22
|
-
spawningAgentType?: string | null;
|
|
23
|
-
/** Target agent type/name requested by the task call. */
|
|
24
|
-
targetAgent: string;
|
|
25
|
-
/** The spawn-plan receipt, when provided. */
|
|
26
|
-
plan?: SpawnPlanReceipt;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
20
|
export type SpawnGateOutcome = "allowed" | "rejected";
|
|
30
21
|
|
|
31
22
|
export interface SpawnGateDecision {
|
|
@@ -102,31 +93,3 @@ export function decide(childCount: number, threshold: number, plan: SpawnPlanRec
|
|
|
102
93
|
export function evaluateSpawnGate(request: SpawnGateRequest): SpawnGateDecision {
|
|
103
94
|
return decide(request.childCount, DEFAULT_SPAWN_THRESHOLD, request.plan);
|
|
104
95
|
}
|
|
105
|
-
|
|
106
|
-
export function evaluateReviewerExploreGate(request: ReviewerExploreGateRequest): SpawnGateDecision {
|
|
107
|
-
if (request.spawningAgentType !== "reviewer" || request.targetAgent !== "explore") {
|
|
108
|
-
return {
|
|
109
|
-
outcome: "allowed",
|
|
110
|
-
reason: "reviewer->explore gate does not apply",
|
|
111
|
-
planRequired: false,
|
|
112
|
-
missingFields: [],
|
|
113
|
-
};
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const missingFields = findMissingPlanFields(request.plan);
|
|
117
|
-
if (missingFields.length > 0) {
|
|
118
|
-
return {
|
|
119
|
-
outcome: "rejected",
|
|
120
|
-
reason: `reviewer->explore spawn requires a complete spawn-plan receipt (${missingFields.join(", ")})`,
|
|
121
|
-
planRequired: true,
|
|
122
|
-
missingFields,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return {
|
|
127
|
-
outcome: "allowed",
|
|
128
|
-
reason: "reviewer->explore spawn has a complete spawn-plan receipt",
|
|
129
|
-
planRequired: true,
|
|
130
|
-
missingFields: [],
|
|
131
|
-
};
|
|
132
|
-
}
|
package/src/task/types.ts
CHANGED
|
@@ -72,7 +72,7 @@ const spawnPlanSchema = z
|
|
|
72
72
|
expectedReceiptShape: z.string(),
|
|
73
73
|
maxInlineTokens: z.number(),
|
|
74
74
|
})
|
|
75
|
-
.describe("justification required before spawning more than four tasks
|
|
75
|
+
.describe("justification required before spawning more than four tasks");
|
|
76
76
|
|
|
77
77
|
const createTaskItemSchema = (_contextEnabled: boolean) =>
|
|
78
78
|
z.object({
|
package/src/tools/ask.ts
CHANGED
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
renderDeepInterviewAskQuestion,
|
|
35
35
|
} from "../deep-interview/render-middleware";
|
|
36
36
|
import type { RenderResultOptions } from "../extensibility/custom-tools/types";
|
|
37
|
-
import { appendOrMergeDeepInterviewRound } from "../gjc-runtime/deep-interview-recorder";
|
|
37
|
+
import { appendOrMergeDeepInterviewRound, syncDeepInterviewRecorderHud } from "../gjc-runtime/deep-interview-recorder";
|
|
38
38
|
import { deepInterviewStatePath } from "../gjc-runtime/deep-interview-runtime";
|
|
39
39
|
import { gateAnswerToResult, questionToGate } from "../modes/shared/agent-wire/deep-interview-gate";
|
|
40
40
|
import { getMarkdownTheme, type Theme, theme } from "../modes/theme/theme";
|
|
@@ -104,6 +104,30 @@ export interface AskToolDetails {
|
|
|
104
104
|
const OTHER_OPTION = "Other (type your own)";
|
|
105
105
|
const RECOMMENDED_SUFFIX = " (Recommended)";
|
|
106
106
|
const DEEP_INTERVIEW_SELECTOR_SCROLL_TITLE_ROWS = Number.MAX_SAFE_INTEGER;
|
|
107
|
+
const DEEP_INTERVIEW_RECORDER_AWAIT_TIMEOUT_MS = 250;
|
|
108
|
+
|
|
109
|
+
function errorMessage(error: unknown): string {
|
|
110
|
+
return error instanceof Error ? error.message : String(error);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
async function awaitDeepInterviewRecorderPersistence(persistence: Promise<void>): Promise<void> {
|
|
114
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
115
|
+
try {
|
|
116
|
+
await Promise.race([
|
|
117
|
+
persistence,
|
|
118
|
+
new Promise<never>((_resolve, reject) => {
|
|
119
|
+
timeout = setTimeout(
|
|
120
|
+
() => reject(new Error(`timed out after ${DEEP_INTERVIEW_RECORDER_AWAIT_TIMEOUT_MS}ms`)),
|
|
121
|
+
DEEP_INTERVIEW_RECORDER_AWAIT_TIMEOUT_MS,
|
|
122
|
+
);
|
|
123
|
+
}),
|
|
124
|
+
]);
|
|
125
|
+
} catch (error) {
|
|
126
|
+
logger.warn(`ask: deep-interview round recording failed: ${errorMessage(error)}`);
|
|
127
|
+
} finally {
|
|
128
|
+
if (timeout) clearTimeout(timeout);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
107
131
|
|
|
108
132
|
function getDoneOptionLabel(): string {
|
|
109
133
|
return `${theme.status.success} Done selecting`;
|
|
@@ -481,11 +505,11 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
481
505
|
): Promise<void> {
|
|
482
506
|
const meta = q.deepInterview;
|
|
483
507
|
if (!meta) return;
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
508
|
+
const cwd = this.session.cwd;
|
|
509
|
+
const sessionId = this.session.getSessionId?.() ?? undefined;
|
|
510
|
+
const statePath = deepInterviewStatePath(cwd, sessionId);
|
|
511
|
+
await awaitDeepInterviewRecorderPersistence(
|
|
512
|
+
appendOrMergeDeepInterviewRound(
|
|
489
513
|
cwd,
|
|
490
514
|
statePath,
|
|
491
515
|
{
|
|
@@ -500,12 +524,10 @@ export class AskTool implements AgentTool<typeof askSchema, AskToolDetails> {
|
|
|
500
524
|
customInput,
|
|
501
525
|
},
|
|
502
526
|
{ sessionId },
|
|
503
|
-
)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
);
|
|
508
|
-
}
|
|
527
|
+
).then(async () => {
|
|
528
|
+
await syncDeepInterviewRecorderHud(cwd, statePath, sessionId);
|
|
529
|
+
}),
|
|
530
|
+
);
|
|
509
531
|
}
|
|
510
532
|
|
|
511
533
|
async execute(
|
package/src/tools/computer.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type { ImageContent } from "@gajae-code/ai";
|
|
|
6
6
|
import { prompt } from "@gajae-code/utils";
|
|
7
7
|
import * as z from "zod/v4";
|
|
8
8
|
import computerDescription from "../prompts/tools/computer.md" with { type: "text" };
|
|
9
|
+
import { resizeImage } from "../utils/image-resize";
|
|
9
10
|
import type { ToolSession } from "./index";
|
|
10
11
|
import type { OutputMeta } from "./output-meta";
|
|
11
12
|
import { ToolAbortError, ToolError, throwIfAborted } from "./tool-errors";
|
|
@@ -174,6 +175,11 @@ let platformOverrideForTests: NodeJS.Platform | undefined;
|
|
|
174
175
|
let archOverrideForTests: NodeJS.Architecture | undefined;
|
|
175
176
|
const screenshotFallbackDirs = new WeakMap<ToolSession, Promise<string>>();
|
|
176
177
|
|
|
178
|
+
const COMPUTER_INLINE_SCREENSHOT_MAX_WIDTH = 1568;
|
|
179
|
+
const COMPUTER_INLINE_SCREENSHOT_MAX_HEIGHT = 1568;
|
|
180
|
+
const COMPUTER_INLINE_SCREENSHOT_PROVIDER_MAX_BYTES = 5 * 1024 * 1024;
|
|
181
|
+
const COMPUTER_INLINE_SCREENSHOT_JPEG_QUALITY = 70;
|
|
182
|
+
|
|
177
183
|
export function setComputerControllerFactoryForTests(factory: ComputerControllerFactory | undefined): void {
|
|
178
184
|
controllerFactory = factory ?? createNativeComputerController;
|
|
179
185
|
}
|
|
@@ -278,6 +284,9 @@ export class ComputerTool implements AgentTool<typeof computerSchema, ComputerTo
|
|
|
278
284
|
if (batchResult.failedStep) {
|
|
279
285
|
details.code = batchResult.failedStep.code;
|
|
280
286
|
details.message = batchResult.failedStep.message;
|
|
287
|
+
if (batchResult.screenshotSource !== undefined) {
|
|
288
|
+
await persistScreenshotFallback(batchResult.screenshotSource, details.screenshot, this.session);
|
|
289
|
+
}
|
|
281
290
|
await writeComputerAuditLog(this.session, details);
|
|
282
291
|
return {
|
|
283
292
|
...toolResult(details).text(`${details.code}: ${details.message}`).done(),
|
|
@@ -285,11 +294,11 @@ export class ComputerTool implements AgentTool<typeof computerSchema, ComputerTo
|
|
|
285
294
|
};
|
|
286
295
|
}
|
|
287
296
|
details.message = describeComputerSuccess(details);
|
|
288
|
-
const image = imageContentFromNativeResult(batchResult.screenshotSource);
|
|
289
297
|
if (batchResult.screenshotSource !== undefined) {
|
|
290
298
|
await persistScreenshotFallback(batchResult.screenshotSource, details.screenshot, this.session);
|
|
291
299
|
details.message = describeComputerSuccess(details);
|
|
292
300
|
}
|
|
301
|
+
const image = await inlineImageContentFromNativeResult(batchResult.screenshotSource, details, this.session);
|
|
293
302
|
await writeComputerAuditLog(this.session, details);
|
|
294
303
|
return image
|
|
295
304
|
? toolResult(details)
|
|
@@ -302,11 +311,11 @@ export class ComputerTool implements AgentTool<typeof computerSchema, ComputerTo
|
|
|
302
311
|
if (screenshot) details.screenshot = screenshot;
|
|
303
312
|
details.status = "success";
|
|
304
313
|
details.message = describeComputerSuccess(details);
|
|
305
|
-
const image = imageContentFromNativeResult(result);
|
|
306
314
|
if (screenshot) {
|
|
307
315
|
await persistScreenshotFallback(result, details.screenshot, this.session);
|
|
308
316
|
details.message = describeComputerSuccess(details);
|
|
309
317
|
}
|
|
318
|
+
const image = await inlineImageContentFromNativeResult(result, details, this.session);
|
|
310
319
|
await writeComputerAuditLog(this.session, details);
|
|
311
320
|
return image
|
|
312
321
|
? toolResult(details)
|
|
@@ -472,7 +481,7 @@ function normalizeScreenshot(value: unknown): ComputerScreenshotDetails | undefi
|
|
|
472
481
|
};
|
|
473
482
|
}
|
|
474
483
|
|
|
475
|
-
function
|
|
484
|
+
function fullResolutionImageContentFromNativeResult(value: unknown): ImageContent | undefined {
|
|
476
485
|
const candidate =
|
|
477
486
|
value && typeof value === "object" && "screenshot" in value
|
|
478
487
|
? (value as { screenshot?: unknown }).screenshot
|
|
@@ -483,13 +492,42 @@ function imageContentFromNativeResult(value: unknown): ImageContent | undefined
|
|
|
483
492
|
return data ? { type: "image", data, mimeType: "image/png" } : undefined;
|
|
484
493
|
}
|
|
485
494
|
|
|
495
|
+
async function inlineImageContentFromNativeResult(
|
|
496
|
+
value: unknown,
|
|
497
|
+
details: ComputerToolDetails,
|
|
498
|
+
session: ToolSession,
|
|
499
|
+
): Promise<ImageContent | undefined> {
|
|
500
|
+
const image = fullResolutionImageContentFromNativeResult(value);
|
|
501
|
+
if (!image) return undefined;
|
|
502
|
+
const maxBytes = getInlineScreenshotMaxBytes(session);
|
|
503
|
+
const originalBytes = Buffer.byteLength(image.data, "base64");
|
|
504
|
+
if (originalBytes <= maxBytes) return image;
|
|
505
|
+
|
|
506
|
+
try {
|
|
507
|
+
const resized = await resizeImage(image, {
|
|
508
|
+
maxWidth: COMPUTER_INLINE_SCREENSHOT_MAX_WIDTH,
|
|
509
|
+
maxHeight: COMPUTER_INLINE_SCREENSHOT_MAX_HEIGHT,
|
|
510
|
+
maxBytes,
|
|
511
|
+
jpegQuality: COMPUTER_INLINE_SCREENSHOT_JPEG_QUALITY,
|
|
512
|
+
});
|
|
513
|
+
if (resized.buffer.length <= maxBytes) {
|
|
514
|
+
return { type: "image", data: resized.data, mimeType: resized.mimeType };
|
|
515
|
+
}
|
|
516
|
+
} catch {
|
|
517
|
+
// Keep the action successful and rely on the full-resolution artifact path below.
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
details.message = `${details.message} Inline screenshot omitted because it could not be bounded below ${formatByteCount(maxBytes)}; use the saved screenshot artifact instead.`;
|
|
521
|
+
return undefined;
|
|
522
|
+
}
|
|
523
|
+
|
|
486
524
|
async function persistScreenshotFallback(
|
|
487
525
|
value: unknown,
|
|
488
526
|
screenshot: ComputerScreenshotDetails | undefined,
|
|
489
527
|
session: ToolSession,
|
|
490
528
|
): Promise<void> {
|
|
491
529
|
if (!screenshot || screenshot.path) return;
|
|
492
|
-
const image =
|
|
530
|
+
const image = fullResolutionImageContentFromNativeResult(value);
|
|
493
531
|
if (!image) return;
|
|
494
532
|
const dir = await getScreenshotFallbackDir(session);
|
|
495
533
|
const filePath = path.join(dir, `computer-${Date.now()}-${Math.random().toString(36).slice(2)}.png`);
|
|
@@ -526,6 +564,22 @@ function getPngByteLength(png: NativeScreenshot["png"]): number | undefined {
|
|
|
526
564
|
return png.byteLength;
|
|
527
565
|
}
|
|
528
566
|
|
|
567
|
+
function getInlineScreenshotMaxBytes(session: Pick<ToolSession, "settings">): number {
|
|
568
|
+
const configured = Number(session.settings.get("computer.screenshotMaxBytes"));
|
|
569
|
+
const finiteConfigured =
|
|
570
|
+
Number.isFinite(configured) && configured > 0
|
|
571
|
+
? Math.floor(configured)
|
|
572
|
+
: COMPUTER_INLINE_SCREENSHOT_PROVIDER_MAX_BYTES;
|
|
573
|
+
return Math.min(finiteConfigured, COMPUTER_INLINE_SCREENSHOT_PROVIDER_MAX_BYTES);
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function formatByteCount(bytes: number): string {
|
|
577
|
+
if (bytes < 1024) return `${bytes} bytes`;
|
|
578
|
+
const kib = bytes / 1024;
|
|
579
|
+
if (kib < 1024) return `${Math.round(kib)} KiB`;
|
|
580
|
+
return `${(kib / 1024).toFixed(1)} MiB`;
|
|
581
|
+
}
|
|
582
|
+
|
|
529
583
|
function mapComputerError(error: unknown, hotkey?: string): { code: string; message: string } {
|
|
530
584
|
if (error instanceof Error && (error.name === "AbortError" || error.name === "TimeoutError")) {
|
|
531
585
|
return {
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { CustomCommand, CustomCommandAPI } from "../../../../extensibility/custom-commands/types";
|
|
2
|
-
import type { HookCommandContext } from "../../../../extensibility/hooks/types";
|
|
3
|
-
export declare class ReviewCommand implements CustomCommand {
|
|
4
|
-
private api;
|
|
5
|
-
name: string;
|
|
6
|
-
description: string;
|
|
7
|
-
constructor(api: CustomCommandAPI);
|
|
8
|
-
execute(args: string[], ctx: HookCommandContext): Promise<string | undefined>;
|
|
9
|
-
}
|
|
10
|
-
export default ReviewCommand;
|