@xfey/tutti 0.1.108 → 0.1.110
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/README.md +5 -1
- package/dist/chat-assistant/diagnostics.d.ts +18 -0
- package/dist/chat-assistant/diagnostics.js +27 -0
- package/dist/chat-assistant/index.d.ts +4 -0
- package/dist/chat-assistant/index.js +45 -21
- package/dist/chat-assistant/reference-freshness-smoke-cli.d.ts +2 -0
- package/dist/chat-assistant/reference-freshness-smoke-cli.js +40 -0
- package/dist/chat-assistant/reference-freshness-smoke.d.ts +23 -0
- package/dist/chat-assistant/reference-freshness-smoke.js +274 -0
- package/dist/collaboration-ingestion/index.d.ts +1 -1
- package/dist/collaboration-ingestion/index.js +1 -1
- package/dist/collaboration-ingestion/managed-reference-sources.d.ts +3 -1
- package/dist/collaboration-ingestion/managed-reference-sources.js +7 -0
- package/dist/collaboration-state/task-compile-context.d.ts +1 -1
- package/dist/providers/invocation-progress.d.ts +10 -0
- package/dist/providers/invocation-progress.js +9 -0
- package/dist/providers/openai/app-server/read-only-procedure.d.ts +2 -0
- package/dist/providers/openai/app-server/read-only-procedure.js +31 -2
- package/dist/providers/openai/app-server/skill-discovery.d.ts +10 -0
- package/dist/providers/openai/app-server/skill-discovery.js +29 -0
- package/dist/providers/openai/app-server/skills.d.ts +4 -16
- package/dist/providers/openai/app-server/skills.js +4 -29
- package/dist/providers/openai/app-server/workspace-write-run.js +13 -19
- package/dist/providers/openai/chat-assistant.js +12 -2
- package/dist/reference-access/message-context.d.ts +6 -0
- package/dist/reference-access/message-context.js +11 -0
- package/dist/reference-access/read-evidence.d.ts +2 -0
- package/dist/reference-access/read-evidence.js +8 -0
- package/dist/server-shell/cli/external-reference-runtime.js +1 -0
- package/dist/server-shell/cli/host-server-runtime.js +2 -0
- package/dist/server-shell/http/routes/agent-context-external-reference-routes.js +30 -4
- package/dist/server-shell/http/routes/agent-context-messages-routes.js +2 -0
- package/dist/server-shell/http/routes/agent-context-reference-routes.js +39 -3
- package/dist/server-shell/http/routes/agent-context-references.d.ts +11 -0
- package/dist/server-shell/http/routes/agent-context-references.js +14 -0
- package/dist/server-shell/http/static-web.js +10 -6
- package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +2 -1
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.d.ts +27 -4
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.js +32 -3
- package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +1 -1
- package/node_modules/@tutti/shared/dist/schemas/api/index.js +1 -1
- package/node_modules/@tutti/shared/dist/schemas/api/messages.d.ts +8 -4
- package/node_modules/@tutti/shared/dist/schemas/api/messages.js +2 -1
- package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.d.ts +5 -3
- package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.js +16 -1
- package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +2 -1
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.d.ts +15 -2
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.js +4 -2
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.d.ts +6 -6
- package/package.json +1 -1
- package/prompts/chat-assistant.md +16 -15
- package/prompts/skills/README.md +1 -1
- package/prompts/skills/read-references/README.md +2 -2
- package/prompts/skills/read-references/SKILL.md +24 -37
- package/web/assets/{homepage-motion-scene-B6yPJnY8.js → homepage-motion-scene-rxYjg-zc.js} +1 -1
- package/web/assets/{index-aQnJ5JoI.js → index-BrKWdtKZ.js} +3 -3
- package/web/index.html +1 -1
|
@@ -24,7 +24,7 @@ export declare function completeTaskCompileContext(db: SqliteDatabase, input: Co
|
|
|
24
24
|
task_changes: string[];
|
|
25
25
|
receipts: {
|
|
26
26
|
compiled_tasks?: {
|
|
27
|
-
status: "failed" | "done" | "
|
|
27
|
+
status: "failed" | "done" | "pending" | "todo" | "running";
|
|
28
28
|
task_id: string;
|
|
29
29
|
title: string;
|
|
30
30
|
summary: string;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export type ProviderInvocationProgress = {
|
|
2
|
+
stage: "provider_queued" | "provider_started" | "context_ready" | "skills_ready" | "turn_started" | "turn_completed" | "provider_failed";
|
|
3
|
+
invocation_ref?: string;
|
|
4
|
+
skill_count?: number;
|
|
5
|
+
completed_tool_count?: number;
|
|
6
|
+
reason_code?: "provider_unavailable" | "app_server_protocol_error" | "auth_failed" | "thread_start_failed" | "turn_failed" | "turn_timeout" | "unexpected_server_request" | "skill_selection_failed" | "output_parse_failed" | "output_validation_failed" | "file_read_not_observed" | "write_detected" | "secret_leak_detected" | "process_lifecycle_failed" | "admission_unavailable";
|
|
7
|
+
};
|
|
8
|
+
export type ProviderInvocationProgressObserver = (event: ProviderInvocationProgress) => void;
|
|
9
|
+
export declare function reportProviderInvocationProgress(observer: ProviderInvocationProgressObserver | undefined, event: ProviderInvocationProgress): void;
|
|
10
|
+
//# sourceMappingURL=invocation-progress.d.ts.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type ProviderInvocationProgressObserver } from "../../invocation-progress.js";
|
|
1
2
|
import { type CodexAppServerLifecycleIncident } from "./invocation-lifecycle.js";
|
|
2
3
|
import { type CodexAppServerInvocationCoordinator, type CodexAppServerInvocationLease } from "./invocation-coordinator.js";
|
|
3
4
|
import { type CodexAppServerRuntimeTelemetryOptions } from "./runtime-telemetry.js";
|
|
@@ -5,6 +6,7 @@ import { type CodexAppServerAgentContextRuntime, type CodexAppServerSkillSelecti
|
|
|
5
6
|
import { type OpenAiTokenUsage } from "../token-usage.js";
|
|
6
7
|
export type CodexAppServerWebSearchMode = "disabled" | "cached" | "live";
|
|
7
8
|
export type CodexAppServerReadOnlyProcedureInput<TOutput> = {
|
|
9
|
+
onProgress?: ProviderInvocationProgressObserver;
|
|
8
10
|
apiKey: string;
|
|
9
11
|
apiBaseUrl?: string;
|
|
10
12
|
defaultModel?: string;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { execFileSync, spawn } from "node:child_process";
|
|
2
2
|
import { mkdirSync, mkdtempSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { discoverCodexAppServerSkills } from "./skill-discovery.js";
|
|
6
|
+
import { reportProviderInvocationProgress, } from "../../invocation-progress.js";
|
|
5
7
|
import { buildCodexAppServerProcessPlan } from "../codex-app-server.js";
|
|
6
8
|
import { DEFAULT_OPENAI_MODEL } from "../model-config.js";
|
|
7
9
|
import { CodexAppServerJsonRpcClient, CodexAppServerProtocolError } from "./json-rpc.js";
|
|
@@ -113,6 +115,7 @@ function structuredOutputDetails(client, text) {
|
|
|
113
115
|
}
|
|
114
116
|
export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
115
117
|
if (options.invocationCoordinator !== undefined && options.invocationLease === undefined) {
|
|
118
|
+
reportProviderInvocationProgress(options.onProgress, { stage: "provider_queued" });
|
|
116
119
|
let lease;
|
|
117
120
|
try {
|
|
118
121
|
lease = await options.invocationCoordinator.acquire();
|
|
@@ -132,6 +135,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
132
135
|
lease.release();
|
|
133
136
|
}
|
|
134
137
|
}
|
|
138
|
+
reportProviderInvocationProgress(options.onProgress, { stage: "provider_started" });
|
|
135
139
|
let skillInputs;
|
|
136
140
|
let contextRuntimeHint;
|
|
137
141
|
try {
|
|
@@ -167,6 +171,13 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
167
171
|
options.builtInSkillsRoot ?? "",
|
|
168
172
|
]));
|
|
169
173
|
}
|
|
174
|
+
if (contextRuntimeHint !== undefined)
|
|
175
|
+
reportProviderInvocationProgress(options.onProgress, {
|
|
176
|
+
stage: "context_ready",
|
|
177
|
+
...(contextRuntimeHint.invocation_ref === undefined
|
|
178
|
+
? {}
|
|
179
|
+
: { invocation_ref: contextRuntimeHint.invocation_ref }),
|
|
180
|
+
});
|
|
170
181
|
const model = options.defaultModel ?? DEFAULT_OPENAI_MODEL;
|
|
171
182
|
const tempRoot = mkdtempSync(join(tmpdir(), "tutti-codex-readonly-procedure-"));
|
|
172
183
|
const codexHome = options.session?.codexHome ?? join(tempRoot, "codex-home");
|
|
@@ -223,7 +234,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
223
234
|
options.userSkillsRoot ?? "",
|
|
224
235
|
options.builtInSkillsRoot ?? "",
|
|
225
236
|
...(contextRuntimeHint?.sensitiveValues ?? []),
|
|
226
|
-
...skillInputs.
|
|
237
|
+
...skillInputs.flatMap((skill) => [skill.path, dirname(skill.path)]),
|
|
227
238
|
];
|
|
228
239
|
const strictLeakCheckValues = [options.apiKey, ...(contextRuntimeHint?.sensitiveValues ?? [])];
|
|
229
240
|
let primaryError;
|
|
@@ -248,6 +259,16 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
248
259
|
if (!isApiKeyAccount(loginResponse)) {
|
|
249
260
|
throw new CodexAppServerReadOnlyProcedureError("auth_failed", "Codex app-server did not accept API key auth.");
|
|
250
261
|
}
|
|
262
|
+
await discoverCodexAppServerSkills({
|
|
263
|
+
client,
|
|
264
|
+
cwd: appServerCwd,
|
|
265
|
+
skills: skillInputs,
|
|
266
|
+
requestTimeoutMs,
|
|
267
|
+
});
|
|
268
|
+
reportProviderInvocationProgress(options.onProgress, {
|
|
269
|
+
stage: "skills_ready",
|
|
270
|
+
skill_count: skillInputs.length,
|
|
271
|
+
});
|
|
251
272
|
let threadId;
|
|
252
273
|
if (options.session?.threadId === undefined) {
|
|
253
274
|
const threadStartResponse = await client.request("thread/start", {
|
|
@@ -298,6 +319,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
298
319
|
modelProvider,
|
|
299
320
|
outputSchema: options.outputSchema,
|
|
300
321
|
}, requestTimeoutMs);
|
|
322
|
+
reportProviderInvocationProgress(options.onProgress, { stage: "turn_started" });
|
|
301
323
|
const turnCompletedPromise = client.waitForNotification((notification) => notification.method === "turn/completed", turnTimeoutMs);
|
|
302
324
|
const serverRequestPromise = client.waitForServerRequest(turnTimeoutMs);
|
|
303
325
|
let completedOrRequest;
|
|
@@ -350,6 +372,10 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
350
372
|
const agentText = client.notifications
|
|
351
373
|
.map((notification) => extractAgentText(notification))
|
|
352
374
|
.join("");
|
|
375
|
+
reportProviderInvocationProgress(options.onProgress, {
|
|
376
|
+
stage: "turn_completed",
|
|
377
|
+
completed_tool_count: structuredOutputDetails(client, agentText).completed_tool_count ?? 0,
|
|
378
|
+
});
|
|
353
379
|
let parsed;
|
|
354
380
|
try {
|
|
355
381
|
parsed = parseCodexAppServerStructuredJson(agentText);
|
|
@@ -408,6 +434,9 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
408
434
|
if (error instanceof CodexAppServerReadOnlyProcedureError) {
|
|
409
435
|
primaryError = error;
|
|
410
436
|
}
|
|
437
|
+
else if (error instanceof CodexAppServerSkillSelectionError) {
|
|
438
|
+
primaryError = new CodexAppServerReadOnlyProcedureError("skill_selection_failed", error.message);
|
|
439
|
+
}
|
|
411
440
|
else if (error instanceof CodexAppServerProtocolError) {
|
|
412
441
|
primaryError = new CodexAppServerReadOnlyProcedureError("app_server_protocol_error", redactWithKnownValues(error.message, sensitiveValues));
|
|
413
442
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type CodexAppServerSkillInput } from "./skills.js";
|
|
2
|
+
export declare function discoverCodexAppServerSkills(options: {
|
|
3
|
+
client: {
|
|
4
|
+
request: (method: string, params: unknown, timeoutMs: number) => Promise<unknown>;
|
|
5
|
+
};
|
|
6
|
+
cwd: string;
|
|
7
|
+
skills: CodexAppServerSkillInput[];
|
|
8
|
+
requestTimeoutMs: number;
|
|
9
|
+
}): Promise<void>;
|
|
10
|
+
//# sourceMappingURL=skill-discovery.d.ts.map
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { realpathSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { isRecord } from "./runtime-helpers.js";
|
|
4
|
+
import { CodexAppServerSkillSelectionError } from "./skills.js";
|
|
5
|
+
// Use the pinned binary's protocol, not fields accepted only by a newer client.
|
|
6
|
+
// Extra roots are process-local; each invocation owns its app-server process.
|
|
7
|
+
export async function discoverCodexAppServerSkills(options) {
|
|
8
|
+
if (options.skills.length === 0)
|
|
9
|
+
return;
|
|
10
|
+
try {
|
|
11
|
+
const cwd = realpathSync(options.cwd);
|
|
12
|
+
await options.client.request("skills/extraRoots/set", { extraRoots: [...new Set(options.skills.map((skill) => dirname(skill.path)))] }, options.requestTimeoutMs);
|
|
13
|
+
const response = await options.client.request("skills/list", { cwds: [cwd], forceReload: true }, options.requestTimeoutMs);
|
|
14
|
+
const entry = isRecord(response) && Array.isArray(response.data)
|
|
15
|
+
? response.data.find((entry) => isRecord(entry) && entry.cwd === cwd)
|
|
16
|
+
: undefined;
|
|
17
|
+
const discovered = isRecord(entry) && Array.isArray(entry.skills) ? entry.skills : [];
|
|
18
|
+
if (!options.skills.every((skill) => discovered.some((candidate) => isRecord(candidate) &&
|
|
19
|
+
candidate.name === skill.name &&
|
|
20
|
+
candidate.path === skill.path &&
|
|
21
|
+
candidate.enabled === true)))
|
|
22
|
+
throw new Error("Selected skill was not discovered");
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// No discovered paths, parser errors or arbitrary RPC error bodies in diagnostics.
|
|
26
|
+
throw new CodexAppServerSkillSelectionError("skill_discovery_failed", "Codex app-server could not discover the selected skills.");
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=skill-discovery.js.map
|
|
@@ -30,27 +30,21 @@ export type CodexAppServerAgentContextTokenRequest = {
|
|
|
30
30
|
export type CodexAppServerAgentContextToken = {
|
|
31
31
|
token: string;
|
|
32
32
|
expires_at?: string;
|
|
33
|
+
invocation_ref?: string;
|
|
33
34
|
};
|
|
34
35
|
export type CodexAppServerAgentContextRuntime = {
|
|
35
36
|
baseUrl: string;
|
|
36
37
|
issueToken: (input: CodexAppServerAgentContextTokenRequest) => CodexAppServerAgentContextToken;
|
|
37
38
|
};
|
|
38
|
-
export type CodexAppServerSkillsListWarmup = {
|
|
39
|
-
cwds: string[];
|
|
40
|
-
forceReload: true;
|
|
41
|
-
perCwdExtraUserRoots: Array<{
|
|
42
|
-
cwd: string;
|
|
43
|
-
extraUserRoots: string[];
|
|
44
|
-
}>;
|
|
45
|
-
};
|
|
46
39
|
export type CodexAppServerContextRuntimeHint = {
|
|
47
40
|
text: string;
|
|
48
41
|
sensitiveValues: string[];
|
|
49
42
|
scopes: CodexAppServerContextApiScope[];
|
|
43
|
+
invocation_ref?: string;
|
|
50
44
|
};
|
|
51
45
|
export declare class CodexAppServerSkillSelectionError extends Error {
|
|
52
|
-
readonly code: "built_in_skill_not_found" | "built_in_skill_invalid";
|
|
53
|
-
constructor(code: "built_in_skill_not_found" | "built_in_skill_invalid", message: string);
|
|
46
|
+
readonly code: "built_in_skill_not_found" | "built_in_skill_invalid" | "skill_discovery_failed";
|
|
47
|
+
constructor(code: "built_in_skill_not_found" | "built_in_skill_invalid" | "skill_discovery_failed", message: string);
|
|
54
48
|
}
|
|
55
49
|
export declare const CODEX_APP_SERVER_SKILL_SELECTIONS: Record<CodexAppServerSkillSelectionName, CodexAppServerSkillSelection>;
|
|
56
50
|
export declare function resolveCodexAppServerSkillSelection(selectionName: CodexAppServerSkillSelectionName): CodexAppServerSkillSelection;
|
|
@@ -60,12 +54,6 @@ export declare function resolveCodexAppServerSkillInputs(options: {
|
|
|
60
54
|
builtInSkillsRoot?: string;
|
|
61
55
|
userSkillsRoot?: string;
|
|
62
56
|
}): CodexAppServerSkillInput[];
|
|
63
|
-
export declare function resolveCodexAppServerSkillsListWarmup(options: {
|
|
64
|
-
cwd: string;
|
|
65
|
-
selectionName?: CodexAppServerSkillSelectionName;
|
|
66
|
-
selection?: CodexAppServerSkillSelection;
|
|
67
|
-
userSkillsRoot?: string;
|
|
68
|
-
}): CodexAppServerSkillsListWarmup | undefined;
|
|
69
57
|
export declare function buildCodexAppServerTurnInput(options: {
|
|
70
58
|
prompt: string;
|
|
71
59
|
skills?: CodexAppServerSkillInput[];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, statSync } from "node:fs";
|
|
1
|
+
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
2
2
|
import { join, resolve } from "node:path";
|
|
3
3
|
import { fileURLToPath } from "node:url";
|
|
4
4
|
import { listUserSkills } from "../../../skills/index.js";
|
|
@@ -121,12 +121,12 @@ function resolveBuiltInSkill(options) {
|
|
|
121
121
|
if (!statSync(skillMdPath).isFile()) {
|
|
122
122
|
throw new CodexAppServerSkillSelectionError("built_in_skill_invalid", `Built-in skill is invalid: ${options.name}`);
|
|
123
123
|
}
|
|
124
|
-
return { name: options.name, path };
|
|
124
|
+
return { name: options.name, path: realpathSync(skillMdPath) };
|
|
125
125
|
}
|
|
126
126
|
function resolveUserSkill(options) {
|
|
127
127
|
return {
|
|
128
128
|
name: options.name,
|
|
129
|
-
path: join(options.userSkillsRoot, options.name),
|
|
129
|
+
path: realpathSync(join(options.userSkillsRoot, options.name, "SKILL.md")),
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
export function resolveCodexAppServerSkillSelection(selectionName) {
|
|
@@ -149,32 +149,6 @@ export function resolveCodexAppServerSkillInputs(options) {
|
|
|
149
149
|
: [];
|
|
150
150
|
return [...builtInSkills, ...userSkills];
|
|
151
151
|
}
|
|
152
|
-
export function resolveCodexAppServerSkillsListWarmup(options) {
|
|
153
|
-
const selection = options.selection ??
|
|
154
|
-
(options.selectionName === undefined
|
|
155
|
-
? {
|
|
156
|
-
builtInSkillNames: [],
|
|
157
|
-
userSkillMode: "disabled",
|
|
158
|
-
contextApiScopes: [],
|
|
159
|
-
}
|
|
160
|
-
: resolveCodexAppServerSkillSelection(options.selectionName));
|
|
161
|
-
if (selection.userSkillMode !== "all_enabled" || options.userSkillsRoot === undefined) {
|
|
162
|
-
return undefined;
|
|
163
|
-
}
|
|
164
|
-
if (listUserSkills(options.userSkillsRoot).length === 0) {
|
|
165
|
-
return undefined;
|
|
166
|
-
}
|
|
167
|
-
return {
|
|
168
|
-
cwds: [options.cwd],
|
|
169
|
-
forceReload: true,
|
|
170
|
-
perCwdExtraUserRoots: [
|
|
171
|
-
{
|
|
172
|
-
cwd: options.cwd,
|
|
173
|
-
extraUserRoots: [options.userSkillsRoot],
|
|
174
|
-
},
|
|
175
|
-
],
|
|
176
|
-
};
|
|
177
|
-
}
|
|
178
152
|
function createTextInputItem(text) {
|
|
179
153
|
return {
|
|
180
154
|
type: "text",
|
|
@@ -228,6 +202,7 @@ export function resolveCodexAppServerContextRuntimeHint(options) {
|
|
|
228
202
|
}),
|
|
229
203
|
sensitiveValues: [issued.token, options.agentContext.baseUrl],
|
|
230
204
|
scopes: selection.contextApiScopes,
|
|
205
|
+
...(issued.invocation_ref === undefined ? {} : { invocation_ref: issued.invocation_ref }),
|
|
231
206
|
};
|
|
232
207
|
}
|
|
233
208
|
//# sourceMappingURL=skills.js.map
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { mkdirSync, mkdtempSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
|
-
import { join } from "node:path";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { discoverCodexAppServerSkills } from "./skill-discovery.js";
|
|
5
6
|
import { buildCodexAppServerProcessPlan } from "../codex-app-server.js";
|
|
6
7
|
import { DEFAULT_OPENAI_MODEL } from "../model-config.js";
|
|
7
8
|
import { CodexAppServerJsonRpcClient, CodexAppServerProtocolError, } from "./json-rpc.js";
|
|
@@ -10,7 +11,7 @@ import { CodexAppServerAdmissionError, } from "./invocation-coordinator.js";
|
|
|
10
11
|
import { createCodexAppServerRuntimeObserver, } from "./runtime-telemetry.js";
|
|
11
12
|
import { createReadOnlySandboxPolicy, createWorkspaceWriteSandboxPolicy, } from "./sandbox-policy.js";
|
|
12
13
|
import { buildCodexAppServerProviderConfigOverrides, resolveCodexAppServerModelProvider, } from "./provider-config.js";
|
|
13
|
-
import { buildCodexAppServerTurnInput, resolveCodexAppServerContextRuntimeHint,
|
|
14
|
+
import { buildCodexAppServerTurnInput, resolveCodexAppServerContextRuntimeHint, resolveCodexAppServerSkillInputs, CodexAppServerSkillSelectionError, } from "./skills.js";
|
|
14
15
|
import { compactText, containsKnownSecret, createCodexSpawnEnv, extractAgentText, extractServerRequestMethod, findJsonObjectCandidates, isApiKeyAccount, readThreadId, readTurnError, readTurnStatus, redactWithKnownValues, } from "./runtime-helpers.js";
|
|
15
16
|
import { extractCodexAppServerTokenUsage } from "../token-usage.js";
|
|
16
17
|
const DEFAULT_REQUEST_TIMEOUT_MS = 60_000;
|
|
@@ -170,7 +171,6 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
let skillInputs;
|
|
173
|
-
let skillsListWarmup;
|
|
174
174
|
let contextRuntimeHint;
|
|
175
175
|
try {
|
|
176
176
|
skillInputs = resolveCodexAppServerSkillInputs({
|
|
@@ -182,13 +182,6 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
182
182
|
? {}
|
|
183
183
|
: { builtInSkillsRoot: options.builtInSkillsRoot }),
|
|
184
184
|
});
|
|
185
|
-
skillsListWarmup = resolveCodexAppServerSkillsListWarmup({
|
|
186
|
-
cwd: options.workspaceRoot,
|
|
187
|
-
...(options.skillSelectionName === undefined
|
|
188
|
-
? {}
|
|
189
|
-
: { selectionName: options.skillSelectionName }),
|
|
190
|
-
...(options.userSkillsRoot === undefined ? {} : { userSkillsRoot: options.userSkillsRoot }),
|
|
191
|
-
});
|
|
192
185
|
contextRuntimeHint = resolveCodexAppServerContextRuntimeHint({
|
|
193
186
|
...(options.approvalContext?.activityRef === undefined
|
|
194
187
|
? {}
|
|
@@ -267,11 +260,7 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
267
260
|
options.userSkillsRoot ?? "",
|
|
268
261
|
options.builtInSkillsRoot ?? "",
|
|
269
262
|
...(contextRuntimeHint?.sensitiveValues ?? []),
|
|
270
|
-
...skillInputs.
|
|
271
|
-
...(skillsListWarmup?.perCwdExtraUserRoots.flatMap((entry) => [
|
|
272
|
-
entry.cwd,
|
|
273
|
-
...entry.extraUserRoots,
|
|
274
|
-
]) ?? []),
|
|
263
|
+
...skillInputs.flatMap((skill) => [skill.path, dirname(skill.path)]),
|
|
275
264
|
];
|
|
276
265
|
const strictLeakCheckValues = [options.apiKey, ...(contextRuntimeHint?.sensitiveValues ?? [])];
|
|
277
266
|
let primaryError;
|
|
@@ -297,9 +286,12 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
297
286
|
if (!isApiKeyAccount(loginResponse)) {
|
|
298
287
|
throw new CodexAppServerWorkspaceWriteRunError("auth_failed", "Codex app-server did not accept API key auth.");
|
|
299
288
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
289
|
+
await discoverCodexAppServerSkills({
|
|
290
|
+
client,
|
|
291
|
+
cwd: options.workspaceRoot,
|
|
292
|
+
skills: skillInputs,
|
|
293
|
+
requestTimeoutMs,
|
|
294
|
+
});
|
|
303
295
|
let threadId;
|
|
304
296
|
if (options.session?.threadId === undefined) {
|
|
305
297
|
const threadStartResponse = await client.request("thread/start", {
|
|
@@ -484,7 +476,9 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
484
476
|
}
|
|
485
477
|
else {
|
|
486
478
|
const message = error instanceof Error ? error.message : String(error);
|
|
487
|
-
primaryError = new CodexAppServerWorkspaceWriteRunError(
|
|
479
|
+
primaryError = new CodexAppServerWorkspaceWriteRunError(error instanceof CodexAppServerSkillSelectionError
|
|
480
|
+
? "skill_selection_failed"
|
|
481
|
+
: "app_server_protocol_error", redactWithKnownValues(message, sensitiveValues), { turn_start_state: turnStartState });
|
|
488
482
|
}
|
|
489
483
|
}
|
|
490
484
|
finally {
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { CHAT_ASSISTANT_OUTPUT_SCHEMA, isChatAssistantOutput, } from "../../chat-assistant/index.js";
|
|
2
2
|
import { readOpenAiApiKeyCredential } from "./credential-store.js";
|
|
3
|
-
import { resolveOpenAiProviderConfig
|
|
4
|
-
import { runCodexAppServerReadOnlyProcedure } from "./app-server/read-only-procedure.js";
|
|
3
|
+
import { resolveOpenAiProviderConfig } from "./provider-config.js";
|
|
4
|
+
import { CodexAppServerReadOnlyProcedureError, runCodexAppServerReadOnlyProcedure, } from "./app-server/read-only-procedure.js";
|
|
5
|
+
import { reportProviderInvocationProgress } from "../invocation-progress.js";
|
|
5
6
|
import { getProjectUserSkillsRoot } from "../../skills/index.js";
|
|
6
7
|
export function createOpenAiReadOnlyChatAssistantModel(options) {
|
|
7
8
|
return {
|
|
8
9
|
async answer(input) {
|
|
9
10
|
const result = await runCodexAppServerReadOnlyProcedure({
|
|
11
|
+
...(input.onProgress === undefined ? {} : { onProgress: input.onProgress }),
|
|
10
12
|
apiKey: options.apiKey,
|
|
11
13
|
workspaceRoot: options.workspaceRoot,
|
|
12
14
|
prompt: input.prompt,
|
|
@@ -37,6 +39,14 @@ export function createOpenAiReadOnlyChatAssistantModel(options) {
|
|
|
37
39
|
...(options.onLifecycleIncident === undefined
|
|
38
40
|
? {}
|
|
39
41
|
: { onLifecycleIncident: options.onLifecycleIncident }),
|
|
42
|
+
}).catch((error) => {
|
|
43
|
+
reportProviderInvocationProgress(input.onProgress, {
|
|
44
|
+
stage: "provider_failed",
|
|
45
|
+
reason_code: error instanceof CodexAppServerReadOnlyProcedureError
|
|
46
|
+
? error.code
|
|
47
|
+
: "provider_unavailable",
|
|
48
|
+
});
|
|
49
|
+
throw error;
|
|
40
50
|
});
|
|
41
51
|
if (options.recordUsage !== undefined && result.usage !== undefined) {
|
|
42
52
|
options.recordUsage({
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import type { MessageProjection } from "@tutti/shared/schemas/api";
|
|
2
|
+
/** Location hints, not access grants or current-read evidence. Never copy private locators. */
|
|
3
|
+
export declare function externalReferenceMessageContext(refs: MessageProjection["refs"]): {
|
|
4
|
+
external_reference_sources?: string[];
|
|
5
|
+
};
|
|
6
|
+
//# sourceMappingURL=message-context.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** Location hints, not access grants or current-read evidence. Never copy private locators. */
|
|
2
|
+
export function externalReferenceMessageContext(refs) {
|
|
3
|
+
const sources = [
|
|
4
|
+
...(refs?.external_references?.map((source) => source.source_ref) ?? []),
|
|
5
|
+
...(refs?.external_reference_reads ?? []),
|
|
6
|
+
];
|
|
7
|
+
return sources.length === 0
|
|
8
|
+
? {}
|
|
9
|
+
: { external_reference_sources: [...new Set(sources)].slice(0, 16) };
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=message-context.js.map
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type { SqliteDatabase } from "../store/index.js";
|
|
2
2
|
import type { ReferenceCopy } from "./cache.js";
|
|
3
3
|
import type { ReferenceInvocation } from "./service.js";
|
|
4
|
+
/** Prior answer context only: a source reference is not proof of freshness in a later turn. */
|
|
5
|
+
export declare function readExternalReferenceReadSources(db: SqliteDatabase, invocationRef: string): string[];
|
|
4
6
|
/** Durable observation, separate from evictable cache metadata; no content or locator. */
|
|
5
7
|
export declare function recordExternalReferenceRead(db: SqliteDatabase, invocation: ReferenceInvocation, copy: ReferenceCopy): void;
|
|
6
8
|
//# sourceMappingURL=read-evidence.d.ts.map
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
/** Prior answer context only: a source reference is not proof of freshness in a later turn. */
|
|
2
|
+
export function readExternalReferenceReadSources(db, invocationRef) {
|
|
3
|
+
return db
|
|
4
|
+
.prepare(`SELECT source_ref FROM external_reference_reads
|
|
5
|
+
WHERE invocation_ref = ? ORDER BY checked_at, source_ref LIMIT 16`)
|
|
6
|
+
.all(invocationRef)
|
|
7
|
+
.map((row) => row.source_ref);
|
|
8
|
+
}
|
|
1
9
|
/** Durable observation, separate from evictable cache metadata; no content or locator. */
|
|
2
10
|
export function recordExternalReferenceRead(db, invocation, copy) {
|
|
3
11
|
const invocationRef = invocation.reference_invocation_ref;
|
|
@@ -17,6 +17,7 @@ export function createExternalReferenceRuntime(options) {
|
|
|
17
17
|
return requestExternalReferences({
|
|
18
18
|
relayUrl: preparation.relay_url,
|
|
19
19
|
body: {
|
|
20
|
+
supports_drive_files: true,
|
|
20
21
|
project_identity: { source: "git_config", project_id: preparation.project_id },
|
|
21
22
|
host_auth: { kind: "host_registration_secret_v1", secret: secret.secret },
|
|
22
23
|
relay_project_ref: relay.relay_project_ref,
|
|
@@ -137,6 +137,7 @@ function createAgentContextRuntime(options) {
|
|
|
137
137
|
return {
|
|
138
138
|
token: token.token,
|
|
139
139
|
expires_at: token.expires_at,
|
|
140
|
+
invocation_ref: token.reference_invocation_ref,
|
|
140
141
|
};
|
|
141
142
|
},
|
|
142
143
|
};
|
|
@@ -419,6 +420,7 @@ export async function startForegroundHostServer(options) {
|
|
|
419
420
|
}),
|
|
420
421
|
});
|
|
421
422
|
const chatAssistant = new ReadOnlyChatAssistant({
|
|
423
|
+
logger: createHostRuntimeLogger({ component: "chat-assistant", logFilePath, now }),
|
|
422
424
|
project: chatAssistantProject,
|
|
423
425
|
store,
|
|
424
426
|
events: workspaceEvents,
|
|
@@ -16,20 +16,43 @@ export function registerAgentContextExternalReferenceRoutes(app, options) {
|
|
|
16
16
|
}, async (request, reply) => {
|
|
17
17
|
reply.header("cache-control", "no-store");
|
|
18
18
|
const invocation = authorizeAgentContextRequest(request, options, "references:external");
|
|
19
|
+
const started = performance.now();
|
|
20
|
+
const diagnosticFields = {
|
|
21
|
+
action,
|
|
22
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
23
|
+
activity_ref: invocation.activity_ref,
|
|
24
|
+
};
|
|
25
|
+
request.log.info({ ...diagnosticFields, stage: "external_reference_request" }, "External Reference request started");
|
|
19
26
|
const service = options.externalReferences;
|
|
20
|
-
if (!service)
|
|
27
|
+
if (!service) {
|
|
28
|
+
request.log.info({
|
|
29
|
+
...diagnosticFields,
|
|
30
|
+
stage: "external_reference_unavailable",
|
|
31
|
+
reason_code: "external_references_not_enabled",
|
|
32
|
+
}, "External Reference service unavailable");
|
|
21
33
|
return { kind: "unavailable", reason: "external_references_not_enabled" };
|
|
34
|
+
}
|
|
22
35
|
const sourceRef = request.query.source_ref;
|
|
23
36
|
if (action === "read" &&
|
|
24
37
|
(sourceRef === undefined || !/^external_ref_[a-f0-9]{32}$/u.test(sourceRef)))
|
|
25
38
|
return { kind: "unavailable", reason: "reference_source_invalid" };
|
|
26
39
|
try {
|
|
27
|
-
if (action === "list")
|
|
28
|
-
|
|
40
|
+
if (action === "list") {
|
|
41
|
+
const items = await service.list(invocation);
|
|
42
|
+
request.log.info({
|
|
43
|
+
...diagnosticFields,
|
|
44
|
+
stage: "external_reference_list",
|
|
45
|
+
source_count: items.length,
|
|
46
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
47
|
+
}, "External Reference sources listed");
|
|
48
|
+
return { items };
|
|
49
|
+
}
|
|
29
50
|
const result = await service.read(invocation, sourceRef);
|
|
30
51
|
const { copy } = result;
|
|
31
52
|
request.log.info({
|
|
32
53
|
stage: "external_reference_read",
|
|
54
|
+
action,
|
|
55
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
33
56
|
invocation_ref: invocation.reference_invocation_ref,
|
|
34
57
|
activity_ref: invocation.activity_ref,
|
|
35
58
|
source_ref: copy.source_ref,
|
|
@@ -53,6 +76,7 @@ export function registerAgentContextExternalReferenceRoutes(app, options) {
|
|
|
53
76
|
kind: text === undefined ? "local_file" : "text",
|
|
54
77
|
...copy,
|
|
55
78
|
cache_hit: result.cache_hit,
|
|
79
|
+
provenance: { origin: "external_reference", verification: "this_invocation" },
|
|
56
80
|
...(safeText === undefined
|
|
57
81
|
? {}
|
|
58
82
|
: {
|
|
@@ -70,7 +94,9 @@ export function registerAgentContextExternalReferenceRoutes(app, options) {
|
|
|
70
94
|
? "reference_cache_unsafe"
|
|
71
95
|
: "reference_read_unavailable";
|
|
72
96
|
request.log.warn({
|
|
73
|
-
stage: "
|
|
97
|
+
stage: "external_reference_unavailable",
|
|
98
|
+
action,
|
|
99
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
74
100
|
invocation_ref: invocation.reference_invocation_ref,
|
|
75
101
|
activity_ref: invocation.activity_ref,
|
|
76
102
|
...(sourceRef === undefined ? {} : { source_ref: sourceRef }),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { readMainChatMessageWindow } from "../../../collaboration-state/index.js";
|
|
2
|
+
import { externalReferenceMessageContext } from "../../../reference-access/message-context.js";
|
|
2
3
|
import { authorizeAgentContextRequest } from "./agent-context-auth.js";
|
|
3
4
|
import { AgentContextBadRequestError } from "./agent-context-errors.js";
|
|
4
5
|
import { boundedLimit, queryString, truncateText } from "./agent-context-query.js";
|
|
@@ -44,6 +45,7 @@ export function registerAgentContextMessageRoutes(app, options) {
|
|
|
44
45
|
},
|
|
45
46
|
body: body.text,
|
|
46
47
|
body_truncated: body.truncated,
|
|
48
|
+
...externalReferenceMessageContext(message.refs),
|
|
47
49
|
...(message.refs?.reference_file === undefined
|
|
48
50
|
? {}
|
|
49
51
|
: {
|
|
@@ -3,11 +3,17 @@ import { VIEWER_HARD_READ_LIMIT_BYTES, WorkspaceOpsError, readReferenceSummaryIn
|
|
|
3
3
|
import { authorizeAgentContextRequest } from "./agent-context-auth.js";
|
|
4
4
|
import { AgentContextBadRequestError } from "./agent-context-errors.js";
|
|
5
5
|
import { boundedLimit, pageByOffset, queryString } from "./agent-context-query.js";
|
|
6
|
-
import { mapReferenceUnavailableReason, referenceListItems, } from "./agent-context-references.js";
|
|
6
|
+
import { mapReferenceUnavailableReason, localReferenceProvenance, managedReferencePaths, referenceListItems, } from "./agent-context-references.js";
|
|
7
7
|
import { AGENT_CONTEXT_BASE_PATH, REFERENCE_FILE_DEFAULT_MAX_BYTES, REFERENCE_FILE_MAX_BYTES, REFERENCES_LIMIT_DEFAULT, REFERENCES_LIMIT_MAX, requireWorkspaceRoot, } from "./agent-context-shared.js";
|
|
8
8
|
export function registerAgentContextReferenceRoutes(app, options) {
|
|
9
9
|
app.get(`${AGENT_CONTEXT_BASE_PATH}/references`, (request) => {
|
|
10
|
-
authorizeAgentContextRequest(request, options, "references:list");
|
|
10
|
+
const invocation = authorizeAgentContextRequest(request, options, "references:list");
|
|
11
|
+
const started = performance.now();
|
|
12
|
+
request.log.info({
|
|
13
|
+
stage: "local_reference_request",
|
|
14
|
+
action: "list",
|
|
15
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
16
|
+
}, "Local Reference request started");
|
|
11
17
|
const query = request.query;
|
|
12
18
|
const limit = boundedLimit(query.limit, {
|
|
13
19
|
default: REFERENCES_LIMIT_DEFAULT,
|
|
@@ -20,6 +26,12 @@ export function registerAgentContextReferenceRoutes(app, options) {
|
|
|
20
26
|
...(cursor === undefined ? {} : { cursor }),
|
|
21
27
|
limit,
|
|
22
28
|
});
|
|
29
|
+
request.log.info({
|
|
30
|
+
stage: "local_reference_list",
|
|
31
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
32
|
+
source_count: page.items.length,
|
|
33
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
34
|
+
}, "Local References listed; remote freshness not checked");
|
|
23
35
|
return {
|
|
24
36
|
items: page.items,
|
|
25
37
|
page: {
|
|
@@ -29,7 +41,13 @@ export function registerAgentContextReferenceRoutes(app, options) {
|
|
|
29
41
|
};
|
|
30
42
|
});
|
|
31
43
|
app.get(`${AGENT_CONTEXT_BASE_PATH}/references/file`, (request) => {
|
|
32
|
-
authorizeAgentContextRequest(request, options, "references:read");
|
|
44
|
+
const invocation = authorizeAgentContextRequest(request, options, "references:read");
|
|
45
|
+
const started = performance.now();
|
|
46
|
+
const diagnosticFields = {
|
|
47
|
+
invocation_ref: invocation.reference_invocation_ref,
|
|
48
|
+
action: "read",
|
|
49
|
+
};
|
|
50
|
+
request.log.info({ ...diagnosticFields, stage: "local_reference_request" }, "Local Reference request started");
|
|
33
51
|
const path = queryString(request.query.path);
|
|
34
52
|
if (path === undefined || path.trim() === "") {
|
|
35
53
|
throw new AgentContextBadRequestError("Reference path is required");
|
|
@@ -49,12 +67,21 @@ export function registerAgentContextReferenceRoutes(app, options) {
|
|
|
49
67
|
entry: file.file,
|
|
50
68
|
summaries: readReferenceSummaryIndex({ workspaceRoot: requireWorkspaceRoot(options) }),
|
|
51
69
|
});
|
|
70
|
+
const provenance = localReferenceProvenance(file.file.path, managedReferencePaths(options));
|
|
71
|
+
request.log.info({
|
|
72
|
+
...diagnosticFields,
|
|
73
|
+
stage: "local_reference_read",
|
|
74
|
+
...provenance,
|
|
75
|
+
result_kind: file.content.kind,
|
|
76
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
77
|
+
}, "Local Reference read; remote freshness not checked");
|
|
52
78
|
if (file.content.kind !== "text") {
|
|
53
79
|
return {
|
|
54
80
|
kind: "unavailable",
|
|
55
81
|
path,
|
|
56
82
|
reason: file.content.reason,
|
|
57
83
|
summary,
|
|
84
|
+
provenance,
|
|
58
85
|
};
|
|
59
86
|
}
|
|
60
87
|
return {
|
|
@@ -62,12 +89,21 @@ export function registerAgentContextReferenceRoutes(app, options) {
|
|
|
62
89
|
path: file.file.path,
|
|
63
90
|
name: file.file.name,
|
|
64
91
|
summary,
|
|
92
|
+
provenance,
|
|
65
93
|
text: redactText(file.content.text),
|
|
66
94
|
truncated: file.content.truncated,
|
|
67
95
|
bytes_read: file.content.bytes_returned,
|
|
68
96
|
};
|
|
69
97
|
}
|
|
70
98
|
catch (error) {
|
|
99
|
+
request.log.warn({
|
|
100
|
+
...diagnosticFields,
|
|
101
|
+
stage: "local_reference_unavailable",
|
|
102
|
+
reason_code: error instanceof WorkspaceOpsError
|
|
103
|
+
? error.code
|
|
104
|
+
: "reference_read_unavailable",
|
|
105
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
106
|
+
}, "Local Reference read unavailable");
|
|
71
107
|
if (error instanceof WorkspaceOpsError) {
|
|
72
108
|
return {
|
|
73
109
|
kind: "unavailable",
|