@xfey/tutti 0.1.107 → 0.1.109
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 +3 -0
- package/dist/chat-assistant/diagnostics.d.ts +17 -0
- package/dist/chat-assistant/diagnostics.js +27 -0
- package/dist/chat-assistant/index.d.ts +4 -0
- package/dist/chat-assistant/index.js +30 -7
- package/dist/collaboration-ingestion/external-source.js +10 -1
- package/dist/control-plane/task-source-context-types.d.ts +2 -1
- package/dist/control-plane/task-source-context.js +7 -0
- 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 +34 -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 +8 -17
- package/dist/providers/openai/app-server/skills.js +16 -31
- package/dist/providers/openai/app-server/workspace-write-run.js +24 -26
- package/dist/providers/openai/chat-assistant.js +12 -2
- package/dist/reference-access/cache.d.ts +50 -0
- package/dist/reference-access/cache.js +268 -0
- package/dist/reference-access/read-evidence.d.ts +6 -0
- package/dist/reference-access/read-evidence.js +15 -0
- package/dist/reference-access/service.d.ts +26 -0
- package/dist/reference-access/service.js +42 -0
- package/dist/server-shell/cli/external-reference-runtime.d.ts +12 -0
- package/dist/server-shell/cli/external-reference-runtime.js +75 -0
- package/dist/server-shell/cli/host-server-runtime.js +19 -1
- package/dist/server-shell/cli/relay-registration.js +1 -0
- package/dist/server-shell/http/create-server.d.ts +2 -0
- package/dist/server-shell/http/create-server.js +5 -4
- package/dist/server-shell/http/routes/agent-context-auth.d.ts +2 -1
- package/dist/server-shell/http/routes/agent-context-auth.js +3 -1
- package/dist/server-shell/http/routes/agent-context-external-reference-routes.d.ts +4 -0
- package/dist/server-shell/http/routes/agent-context-external-reference-routes.js +108 -0
- package/dist/server-shell/http/routes/agent-context-shared.d.ts +2 -0
- package/dist/server-shell/http/routes/agent-context.js +3 -1
- package/dist/server-shell/http/static-web.js +10 -6
- package/dist/workspace-ops/project-workspace.js +6 -6
- package/dist/workspace-ops/reference-cache-policy.d.ts +6 -0
- package/dist/workspace-ops/reference-cache-policy.js +79 -0
- package/migrations/0021_external_reference_reads.sql +11 -0
- package/node_modules/@tutti/relay-client/dist/external-references.d.ts +11 -0
- package/node_modules/@tutti/relay-client/dist/external-references.js +63 -0
- package/node_modules/@tutti/relay-client/dist/host-control.d.ts +1 -0
- package/node_modules/@tutti/relay-client/dist/host-control.js +1 -0
- package/node_modules/@tutti/relay-client/dist/index.d.ts +1 -0
- package/node_modules/@tutti/relay-client/dist/index.js +1 -0
- package/node_modules/@tutti/shared/dist/schemas/api/clarifications.d.ts +6 -0
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.d.ts +23 -0
- package/node_modules/@tutti/shared/dist/schemas/api/external-references.js +21 -0
- package/node_modules/@tutti/shared/dist/schemas/api/feishu-documents.d.ts +6 -1
- package/node_modules/@tutti/shared/dist/schemas/api/feishu-documents.js +16 -3
- package/node_modules/@tutti/shared/dist/schemas/api/index.d.ts +3 -2
- package/node_modules/@tutti/shared/dist/schemas/api/index.js +2 -1
- package/node_modules/@tutti/shared/dist/schemas/api/messages.d.ts +24 -0
- package/node_modules/@tutti/shared/dist/schemas/api/messages.js +2 -0
- package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.d.ts +7 -1
- package/node_modules/@tutti/shared/dist/schemas/api/platform-sessions.js +17 -3
- package/node_modules/@tutti/shared/dist/schemas/api/viewer-reference.d.ts +6 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.d.ts +109 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/external-references.js +70 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/feishu-documents.d.ts +11 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/feishu-documents.js +5 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.d.ts +1 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/index.js +1 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.d.ts +36 -0
- package/node_modules/@tutti/shared/dist/schemas/internal/platform-integration.js +3 -0
- package/package.json +1 -1
- package/prompts/skills/read-references/SKILL.md +32 -3
- package/web/assets/{homepage-motion-scene-CFs5rMWq.js → homepage-motion-scene-B6yPJnY8.js} +1 -1
- package/web/assets/{index-BmiLZmaV.js → index-aQnJ5JoI.js} +3 -3
- package/web/index.html +1 -1
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
`apps/server` 是 Tutti 的项目 host service,也是机器级 Local Console 的进程入口。
|
|
6
6
|
|
|
7
|
+
`0.1.109` 的 Codex skill 接入统一验证 `skills/extraRoots/set` / `skills/list` 与实际 `SKILL.md` 文件身份;Chat 的安全阶段日志可通过 invocation ref 关联外部 Reference 读取。版本发布、测试 Host 切换和飞书真人门禁分别记录于[按需资料计划](../../docs/roadmap/feishu-on-demand-reference-plan.md),不以安装版本代替实际读取证据。
|
|
8
|
+
|
|
7
9
|
它负责:
|
|
8
10
|
|
|
9
11
|
- `tutti launch` 后的项目绑定、恢复和接管入口
|
|
@@ -15,6 +17,7 @@
|
|
|
15
17
|
- host 到 Relay 的连接与工作区加入协作
|
|
16
18
|
- machine-local `standalone | feishu` Project Surface authority,以及Standalone browser session / Feishu trusted integration的双向Host gate
|
|
17
19
|
- Feishu主群聊Chat Assistant canonical response与Host-owned durable outbound handoff intent
|
|
20
|
+
- Feishu 外部来源的按需读取、受控临时 cache、invocation 版本和 SQLite 读取证据;正式 Reference/Standalone 上传与历史 snapshot 保持,见 [external-reference-access.md](../../docs/system/external-reference-access.md)
|
|
18
21
|
|
|
19
22
|
## 结构
|
|
20
23
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ProviderInvocationProgress } from "../providers/invocation-progress.js";
|
|
2
|
+
export type ChatAssistantLogger = {
|
|
3
|
+
info: (fields: Record<string, unknown>, message: string) => void;
|
|
4
|
+
warn: (fields: Record<string, unknown>, message: string) => void;
|
|
5
|
+
};
|
|
6
|
+
type ChatAssistantProgress = ProviderInvocationProgress | {
|
|
7
|
+
stage: "queued" | "inspection_wait" | "model_requested" | "reply_persisted" | "failed" | "skipped";
|
|
8
|
+
reason_code?: "queue_full" | "stopping" | "context_unavailable" | "answer_failed" | "reply_failed";
|
|
9
|
+
};
|
|
10
|
+
export declare function createChatAssistantDiagnostics(options: {
|
|
11
|
+
logger?: ChatAssistantLogger;
|
|
12
|
+
projectId: string;
|
|
13
|
+
triggerMessageId: string;
|
|
14
|
+
scope: string;
|
|
15
|
+
}): (event: ChatAssistantProgress) => void;
|
|
16
|
+
export {};
|
|
17
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { createEventRef } from "@tutti/shared/ids";
|
|
2
|
+
export function createChatAssistantDiagnostics(options) {
|
|
3
|
+
const requestRef = createEventRef();
|
|
4
|
+
const started = performance.now();
|
|
5
|
+
return (event) => {
|
|
6
|
+
try {
|
|
7
|
+
const fields = {
|
|
8
|
+
project_id: options.projectId,
|
|
9
|
+
chat_request_ref: requestRef,
|
|
10
|
+
trigger_message_ref: options.triggerMessageId,
|
|
11
|
+
scope: options.scope,
|
|
12
|
+
elapsed_ms: Math.round(performance.now() - started),
|
|
13
|
+
...event,
|
|
14
|
+
};
|
|
15
|
+
if (event.stage === "failed" || event.stage === "provider_failed") {
|
|
16
|
+
options.logger?.warn(fields, "Chat assistant progress");
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
options.logger?.info(fields, "Chat assistant progress");
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
/* Logging must not replace the answer or interrupt queue draining. */
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ProjectId } from "@tutti/shared/ids";
|
|
2
2
|
import type { MessageProjection } from "@tutti/shared/schemas/api";
|
|
3
|
+
import { type ChatAssistantLogger } from "./diagnostics.js";
|
|
4
|
+
import type { ProviderInvocationProgressObserver } from "../providers/invocation-progress.js";
|
|
3
5
|
import { type HostProjectStore, type SqliteDatabase } from "../store/index.js";
|
|
4
6
|
import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
|
|
5
7
|
export declare const CHAT_ASSISTANT_OUTPUT_SCHEMA: {
|
|
@@ -19,6 +21,7 @@ export type ChatAssistantOutput = {
|
|
|
19
21
|
};
|
|
20
22
|
export type ChatAssistantPromptInput = {
|
|
21
23
|
prompt: string;
|
|
24
|
+
onProgress?: ProviderInvocationProgressObserver;
|
|
22
25
|
};
|
|
23
26
|
export type ReadOnlyChatAssistantModel = {
|
|
24
27
|
answer: (input: ChatAssistantPromptInput) => Promise<ChatAssistantOutput>;
|
|
@@ -35,6 +38,7 @@ export type ReadOnlyChatAssistantHandle = {
|
|
|
35
38
|
stopAccepting: () => void;
|
|
36
39
|
};
|
|
37
40
|
export type ReadOnlyChatAssistantOptions = {
|
|
41
|
+
logger?: ChatAssistantLogger;
|
|
38
42
|
project: {
|
|
39
43
|
project_id: ProjectId;
|
|
40
44
|
display_name: string;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createChatAssistantDiagnostics } from "./diagnostics.js";
|
|
1
2
|
import { createClarificationRoundAgentMessage, createMainChatAgentMessage, readClarificationRoundMessagesWindow, readClarificationRoundProjection, readMainChatMessageWindow, readMainChatMessagesBeforeCursor, readMessageProjectionById, } from "../collaboration-state/index.js";
|
|
2
3
|
import { renderPromptTemplate } from "../prompt-templates/index.js";
|
|
3
4
|
import { withHostStoreTransaction, } from "../store/index.js";
|
|
@@ -111,13 +112,21 @@ export class ReadOnlyChatAssistant {
|
|
|
111
112
|
if (!this.accepting) {
|
|
112
113
|
return { triggered: false };
|
|
113
114
|
}
|
|
115
|
+
const report = createChatAssistantDiagnostics({
|
|
116
|
+
...(this.options.logger === undefined ? {} : { logger: this.options.logger }),
|
|
117
|
+
projectId: this.options.project.project_id,
|
|
118
|
+
triggerMessageId: input.message.id,
|
|
119
|
+
scope: input.message.scope.kind,
|
|
120
|
+
});
|
|
114
121
|
if (this.pending.length >= this.maxQueuedMentions) {
|
|
122
|
+
report({ stage: "failed", reason_code: "queue_full" });
|
|
115
123
|
this.appendAssistantMessageForTrigger(input.message, assistantFailureMessage(), {
|
|
116
124
|
scratchpad_source: "exclude",
|
|
117
125
|
});
|
|
118
126
|
return { triggered: true };
|
|
119
127
|
}
|
|
120
|
-
|
|
128
|
+
report({ stage: "queued" });
|
|
129
|
+
this.pending.push({ message: input.message, report });
|
|
121
130
|
this.pump();
|
|
122
131
|
return { triggered: true };
|
|
123
132
|
}
|
|
@@ -132,7 +141,8 @@ export class ReadOnlyChatAssistant {
|
|
|
132
141
|
return;
|
|
133
142
|
}
|
|
134
143
|
this.accepting = false;
|
|
135
|
-
for (const message of this.pending.splice(0)) {
|
|
144
|
+
for (const { message, report } of this.pending.splice(0)) {
|
|
145
|
+
report({ stage: "skipped", reason_code: "stopping" });
|
|
136
146
|
this.appendAssistantMessageForTrigger(message, assistantFailureMessage(), {
|
|
137
147
|
scratchpad_source: "exclude",
|
|
138
148
|
});
|
|
@@ -143,12 +153,14 @@ export class ReadOnlyChatAssistant {
|
|
|
143
153
|
if (this.active !== null) {
|
|
144
154
|
return;
|
|
145
155
|
}
|
|
146
|
-
const
|
|
147
|
-
if (
|
|
156
|
+
const pending = this.pending.shift();
|
|
157
|
+
if (pending === undefined) {
|
|
148
158
|
this.resolveIdleIfNeeded();
|
|
149
159
|
return;
|
|
150
160
|
}
|
|
151
|
-
const task = this.respond(message).catch(() =>
|
|
161
|
+
const task = this.respond(pending.message, pending.report).catch(() => {
|
|
162
|
+
pending.report({ stage: "failed", reason_code: "reply_failed" });
|
|
163
|
+
});
|
|
152
164
|
this.active = task;
|
|
153
165
|
void task.finally(() => {
|
|
154
166
|
if (this.active === task) {
|
|
@@ -311,23 +323,34 @@ export class ReadOnlyChatAssistant {
|
|
|
311
323
|
}
|
|
312
324
|
this.appendMainChatAssistantMessage(triggerMessage, body, refs);
|
|
313
325
|
}
|
|
314
|
-
async respond(triggerMessage) {
|
|
326
|
+
async respond(triggerMessage, report) {
|
|
327
|
+
let answer;
|
|
315
328
|
try {
|
|
329
|
+
report({ stage: "inspection_wait" });
|
|
316
330
|
await this.options.waitForInspectionAvailability?.();
|
|
317
331
|
const prompt = this.renderPrompt(triggerMessage);
|
|
318
332
|
if (prompt === null) {
|
|
333
|
+
report({ stage: "skipped", reason_code: "context_unavailable" });
|
|
319
334
|
return;
|
|
320
335
|
}
|
|
336
|
+
report({ stage: "model_requested" });
|
|
321
337
|
const output = await this.options.model.answer({
|
|
322
338
|
prompt,
|
|
339
|
+
onProgress: report,
|
|
323
340
|
});
|
|
324
|
-
|
|
341
|
+
answer = normalizeAnswer(output.answer);
|
|
325
342
|
}
|
|
326
343
|
catch {
|
|
344
|
+
report({ stage: "failed", reason_code: "answer_failed" });
|
|
327
345
|
this.appendAssistantMessageForTrigger(triggerMessage, assistantFailureMessage(), {
|
|
328
346
|
scratchpad_source: "exclude",
|
|
329
347
|
});
|
|
348
|
+
report({ stage: "reply_persisted" });
|
|
349
|
+
return;
|
|
330
350
|
}
|
|
351
|
+
// Persistence/outbound-hook failure must not retry by creating a second reply.
|
|
352
|
+
this.appendAssistantMessageForTrigger(triggerMessage, answer);
|
|
353
|
+
report({ stage: "reply_persisted" });
|
|
331
354
|
}
|
|
332
355
|
}
|
|
333
356
|
//# sourceMappingURL=index.js.map
|
|
@@ -86,7 +86,13 @@ export function ingestTrustedConversationSource(options) {
|
|
|
86
86
|
account_ref: options.command.actor.account_ref,
|
|
87
87
|
display_name: options.command.actor.display_name,
|
|
88
88
|
},
|
|
89
|
-
materialize: () => ({
|
|
89
|
+
materialize: () => ({
|
|
90
|
+
kind: "message",
|
|
91
|
+
body: source.content.text,
|
|
92
|
+
...(source.external_references === undefined
|
|
93
|
+
? {}
|
|
94
|
+
: { refs: { external_references: source.external_references } }),
|
|
95
|
+
}),
|
|
90
96
|
idempotencyContext: {
|
|
91
97
|
kind: "external_conversation_source",
|
|
92
98
|
platform: source.locator.conversation.installation.platform,
|
|
@@ -94,6 +100,9 @@ export function ingestTrustedConversationSource(options) {
|
|
|
94
100
|
external_message_ref: source.locator.message_ref,
|
|
95
101
|
binding_ref: options.command.binding_ref,
|
|
96
102
|
receipt_key: options.command.receipt.receipt_key,
|
|
103
|
+
...(source.external_references === undefined
|
|
104
|
+
? {}
|
|
105
|
+
: { external_references: source.external_references }),
|
|
97
106
|
},
|
|
98
107
|
afterCreate: (tx, message) => insertExternalSourceEvidence(tx, options.command, message),
|
|
99
108
|
...(options.scratchpadSource === undefined
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import type { ClarificationRequestPayload } from "@tutti/shared/schemas/api";
|
|
1
|
+
import type { ClarificationRequestPayload, ExternalReferenceDescriptor } from "@tutti/shared/schemas/api";
|
|
2
2
|
export type PromptSourceMessage = {
|
|
3
3
|
role: "human" | "tutti";
|
|
4
4
|
author_display_name?: string;
|
|
5
5
|
text: string;
|
|
6
|
+
external_references?: ExternalReferenceDescriptor[];
|
|
6
7
|
};
|
|
7
8
|
export type TaskCompileClarificationContext = {
|
|
8
9
|
request_payload: ClarificationRequestPayload;
|
|
@@ -16,6 +16,13 @@ function promptSourceMessage(db, source) {
|
|
|
16
16
|
? {}
|
|
17
17
|
: { author_display_name: displayName }),
|
|
18
18
|
text: messageTextForModel(message),
|
|
19
|
+
...(message.refs?.external_references === undefined
|
|
20
|
+
? {}
|
|
21
|
+
: {
|
|
22
|
+
external_references: message.refs.external_references.map((reference) => ({
|
|
23
|
+
...reference,
|
|
24
|
+
})),
|
|
25
|
+
}),
|
|
19
26
|
};
|
|
20
27
|
}
|
|
21
28
|
if (message.message_kind === "agent_text" && message.author.kind === "agent") {
|
|
@@ -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 {
|
|
@@ -145,6 +149,9 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
145
149
|
: { builtInSkillsRoot: options.builtInSkillsRoot }),
|
|
146
150
|
});
|
|
147
151
|
contextRuntimeHint = resolveCodexAppServerContextRuntimeHint({
|
|
152
|
+
...(options.runtimeTelemetry?.activityRef === undefined
|
|
153
|
+
? {}
|
|
154
|
+
: { activityRef: options.runtimeTelemetry.activityRef }),
|
|
148
155
|
...(options.skillSelectionName === undefined
|
|
149
156
|
? {}
|
|
150
157
|
: { selectionName: options.skillSelectionName }),
|
|
@@ -164,6 +171,13 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
164
171
|
options.builtInSkillsRoot ?? "",
|
|
165
172
|
]));
|
|
166
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
|
+
});
|
|
167
181
|
const model = options.defaultModel ?? DEFAULT_OPENAI_MODEL;
|
|
168
182
|
const tempRoot = mkdtempSync(join(tmpdir(), "tutti-codex-readonly-procedure-"));
|
|
169
183
|
const codexHome = options.session?.codexHome ?? join(tempRoot, "codex-home");
|
|
@@ -220,7 +234,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
220
234
|
options.userSkillsRoot ?? "",
|
|
221
235
|
options.builtInSkillsRoot ?? "",
|
|
222
236
|
...(contextRuntimeHint?.sensitiveValues ?? []),
|
|
223
|
-
...skillInputs.
|
|
237
|
+
...skillInputs.flatMap((skill) => [skill.path, dirname(skill.path)]),
|
|
224
238
|
];
|
|
225
239
|
const strictLeakCheckValues = [options.apiKey, ...(contextRuntimeHint?.sensitiveValues ?? [])];
|
|
226
240
|
let primaryError;
|
|
@@ -245,6 +259,16 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
245
259
|
if (!isApiKeyAccount(loginResponse)) {
|
|
246
260
|
throw new CodexAppServerReadOnlyProcedureError("auth_failed", "Codex app-server did not accept API key auth.");
|
|
247
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
|
+
});
|
|
248
272
|
let threadId;
|
|
249
273
|
if (options.session?.threadId === undefined) {
|
|
250
274
|
const threadStartResponse = await client.request("thread/start", {
|
|
@@ -295,6 +319,7 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
295
319
|
modelProvider,
|
|
296
320
|
outputSchema: options.outputSchema,
|
|
297
321
|
}, requestTimeoutMs);
|
|
322
|
+
reportProviderInvocationProgress(options.onProgress, { stage: "turn_started" });
|
|
298
323
|
const turnCompletedPromise = client.waitForNotification((notification) => notification.method === "turn/completed", turnTimeoutMs);
|
|
299
324
|
const serverRequestPromise = client.waitForServerRequest(turnTimeoutMs);
|
|
300
325
|
let completedOrRequest;
|
|
@@ -347,6 +372,10 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
347
372
|
const agentText = client.notifications
|
|
348
373
|
.map((notification) => extractAgentText(notification))
|
|
349
374
|
.join("");
|
|
375
|
+
reportProviderInvocationProgress(options.onProgress, {
|
|
376
|
+
stage: "turn_completed",
|
|
377
|
+
completed_tool_count: structuredOutputDetails(client, agentText).completed_tool_count ?? 0,
|
|
378
|
+
});
|
|
350
379
|
let parsed;
|
|
351
380
|
try {
|
|
352
381
|
parsed = parseCodexAppServerStructuredJson(agentText);
|
|
@@ -405,6 +434,9 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
405
434
|
if (error instanceof CodexAppServerReadOnlyProcedureError) {
|
|
406
435
|
primaryError = error;
|
|
407
436
|
}
|
|
437
|
+
else if (error instanceof CodexAppServerSkillSelectionError) {
|
|
438
|
+
primaryError = new CodexAppServerReadOnlyProcedureError("skill_selection_failed", error.message);
|
|
439
|
+
}
|
|
408
440
|
else if (error instanceof CodexAppServerProtocolError) {
|
|
409
441
|
primaryError = new CodexAppServerReadOnlyProcedureError("app_server_protocol_error", redactWithKnownValues(error.message, sensitiveValues));
|
|
410
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
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ActivityRef } from "@tutti/shared/ids";
|
|
1
2
|
export type CodexAppServerSkillInput = {
|
|
2
3
|
name: string;
|
|
3
4
|
path: string;
|
|
@@ -15,40 +16,35 @@ export type CodexAppServerSkillInputItem = {
|
|
|
15
16
|
export type CodexAppServerTurnInputItem = CodexAppServerSkillInputItem | CodexAppServerTextInputItem;
|
|
16
17
|
export type CodexAppServerBuiltInSkillName = "read-main-chat" | "read-project-docs" | "read-scratchpad" | "read-worklist" | "read-references" | "read-user-skills";
|
|
17
18
|
export type CodexAppServerSkillSelectionName = "chat_assistant" | "project_context_bootstrap" | "task_compile" | "context_sync" | "reference_summary_refresh" | "workspace_write_run" | "follow_up_run";
|
|
18
|
-
export type CodexAppServerContextApiScope = "messages:read" | "project-docs:list" | "project-docs:read" | "scratchpad:read" | "worklist:read" | "task:read" | "references:list" | "references:read" | "skills:list" | "skills:read";
|
|
19
|
+
export type CodexAppServerContextApiScope = "messages:read" | "project-docs:list" | "project-docs:read" | "scratchpad:read" | "worklist:read" | "task:read" | "references:list" | "references:read" | "references:external" | "skills:list" | "skills:read";
|
|
19
20
|
export type CodexAppServerSkillSelection = {
|
|
20
21
|
builtInSkillNames: CodexAppServerBuiltInSkillName[];
|
|
21
22
|
userSkillMode: "disabled" | "all_enabled";
|
|
22
23
|
contextApiScopes: CodexAppServerContextApiScope[];
|
|
23
24
|
};
|
|
24
25
|
export type CodexAppServerAgentContextTokenRequest = {
|
|
26
|
+
activityRef?: ActivityRef;
|
|
25
27
|
selectionName: CodexAppServerSkillSelectionName;
|
|
26
28
|
scopes: CodexAppServerContextApiScope[];
|
|
27
29
|
};
|
|
28
30
|
export type CodexAppServerAgentContextToken = {
|
|
29
31
|
token: string;
|
|
30
32
|
expires_at?: string;
|
|
33
|
+
invocation_ref?: string;
|
|
31
34
|
};
|
|
32
35
|
export type CodexAppServerAgentContextRuntime = {
|
|
33
36
|
baseUrl: string;
|
|
34
37
|
issueToken: (input: CodexAppServerAgentContextTokenRequest) => CodexAppServerAgentContextToken;
|
|
35
38
|
};
|
|
36
|
-
export type CodexAppServerSkillsListWarmup = {
|
|
37
|
-
cwds: string[];
|
|
38
|
-
forceReload: true;
|
|
39
|
-
perCwdExtraUserRoots: Array<{
|
|
40
|
-
cwd: string;
|
|
41
|
-
extraUserRoots: string[];
|
|
42
|
-
}>;
|
|
43
|
-
};
|
|
44
39
|
export type CodexAppServerContextRuntimeHint = {
|
|
45
40
|
text: string;
|
|
46
41
|
sensitiveValues: string[];
|
|
47
42
|
scopes: CodexAppServerContextApiScope[];
|
|
43
|
+
invocation_ref?: string;
|
|
48
44
|
};
|
|
49
45
|
export declare class CodexAppServerSkillSelectionError extends Error {
|
|
50
|
-
readonly code: "built_in_skill_not_found" | "built_in_skill_invalid";
|
|
51
|
-
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);
|
|
52
48
|
}
|
|
53
49
|
export declare const CODEX_APP_SERVER_SKILL_SELECTIONS: Record<CodexAppServerSkillSelectionName, CodexAppServerSkillSelection>;
|
|
54
50
|
export declare function resolveCodexAppServerSkillSelection(selectionName: CodexAppServerSkillSelectionName): CodexAppServerSkillSelection;
|
|
@@ -58,12 +54,6 @@ export declare function resolveCodexAppServerSkillInputs(options: {
|
|
|
58
54
|
builtInSkillsRoot?: string;
|
|
59
55
|
userSkillsRoot?: string;
|
|
60
56
|
}): CodexAppServerSkillInput[];
|
|
61
|
-
export declare function resolveCodexAppServerSkillsListWarmup(options: {
|
|
62
|
-
cwd: string;
|
|
63
|
-
selectionName?: CodexAppServerSkillSelectionName;
|
|
64
|
-
selection?: CodexAppServerSkillSelection;
|
|
65
|
-
userSkillsRoot?: string;
|
|
66
|
-
}): CodexAppServerSkillsListWarmup | undefined;
|
|
67
57
|
export declare function buildCodexAppServerTurnInput(options: {
|
|
68
58
|
prompt: string;
|
|
69
59
|
skills?: CodexAppServerSkillInput[];
|
|
@@ -76,6 +66,7 @@ export declare function buildCodexAppServerContextRuntimeHint(options: {
|
|
|
76
66
|
expiresAt?: string;
|
|
77
67
|
}): string;
|
|
78
68
|
export declare function resolveCodexAppServerContextRuntimeHint(options: {
|
|
69
|
+
activityRef?: ActivityRef;
|
|
79
70
|
selectionName?: CodexAppServerSkillSelectionName;
|
|
80
71
|
agentContext?: CodexAppServerAgentContextRuntime;
|
|
81
72
|
}): CodexAppServerContextRuntimeHint | undefined;
|
|
@@ -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";
|
|
@@ -30,6 +30,7 @@ export const CODEX_APP_SERVER_SKILL_SELECTIONS = {
|
|
|
30
30
|
"task:read",
|
|
31
31
|
"references:list",
|
|
32
32
|
"references:read",
|
|
33
|
+
"references:external",
|
|
33
34
|
"skills:list",
|
|
34
35
|
"skills:read",
|
|
35
36
|
],
|
|
@@ -42,6 +43,7 @@ export const CODEX_APP_SERVER_SKILL_SELECTIONS = {
|
|
|
42
43
|
"project-docs:read",
|
|
43
44
|
"references:list",
|
|
44
45
|
"references:read",
|
|
46
|
+
"references:external",
|
|
45
47
|
],
|
|
46
48
|
},
|
|
47
49
|
task_compile: {
|
|
@@ -54,6 +56,7 @@ export const CODEX_APP_SERVER_SKILL_SELECTIONS = {
|
|
|
54
56
|
"task:read",
|
|
55
57
|
"references:list",
|
|
56
58
|
"references:read",
|
|
59
|
+
"references:external",
|
|
57
60
|
],
|
|
58
61
|
},
|
|
59
62
|
context_sync: {
|
|
@@ -74,23 +77,29 @@ export const CODEX_APP_SERVER_SKILL_SELECTIONS = {
|
|
|
74
77
|
contextApiScopes: [],
|
|
75
78
|
},
|
|
76
79
|
workspace_write_run: {
|
|
77
|
-
builtInSkillNames: ["read-project-docs", "read-worklist"],
|
|
80
|
+
builtInSkillNames: ["read-project-docs", "read-worklist", "read-references"],
|
|
78
81
|
userSkillMode: "all_enabled",
|
|
79
82
|
contextApiScopes: [
|
|
80
83
|
"project-docs:list",
|
|
81
84
|
"project-docs:read",
|
|
82
85
|
"worklist:read",
|
|
83
86
|
"task:read",
|
|
87
|
+
"references:list",
|
|
88
|
+
"references:read",
|
|
89
|
+
"references:external",
|
|
84
90
|
],
|
|
85
91
|
},
|
|
86
92
|
follow_up_run: {
|
|
87
|
-
builtInSkillNames: ["read-project-docs", "read-worklist"],
|
|
93
|
+
builtInSkillNames: ["read-project-docs", "read-worklist", "read-references"],
|
|
88
94
|
userSkillMode: "all_enabled",
|
|
89
95
|
contextApiScopes: [
|
|
90
96
|
"project-docs:list",
|
|
91
97
|
"project-docs:read",
|
|
92
98
|
"worklist:read",
|
|
93
99
|
"task:read",
|
|
100
|
+
"references:list",
|
|
101
|
+
"references:read",
|
|
102
|
+
"references:external",
|
|
94
103
|
],
|
|
95
104
|
},
|
|
96
105
|
};
|
|
@@ -112,12 +121,12 @@ function resolveBuiltInSkill(options) {
|
|
|
112
121
|
if (!statSync(skillMdPath).isFile()) {
|
|
113
122
|
throw new CodexAppServerSkillSelectionError("built_in_skill_invalid", `Built-in skill is invalid: ${options.name}`);
|
|
114
123
|
}
|
|
115
|
-
return { name: options.name, path };
|
|
124
|
+
return { name: options.name, path: realpathSync(skillMdPath) };
|
|
116
125
|
}
|
|
117
126
|
function resolveUserSkill(options) {
|
|
118
127
|
return {
|
|
119
128
|
name: options.name,
|
|
120
|
-
path: join(options.userSkillsRoot, options.name),
|
|
129
|
+
path: realpathSync(join(options.userSkillsRoot, options.name, "SKILL.md")),
|
|
121
130
|
};
|
|
122
131
|
}
|
|
123
132
|
export function resolveCodexAppServerSkillSelection(selectionName) {
|
|
@@ -140,32 +149,6 @@ export function resolveCodexAppServerSkillInputs(options) {
|
|
|
140
149
|
: [];
|
|
141
150
|
return [...builtInSkills, ...userSkills];
|
|
142
151
|
}
|
|
143
|
-
export function resolveCodexAppServerSkillsListWarmup(options) {
|
|
144
|
-
const selection = options.selection ??
|
|
145
|
-
(options.selectionName === undefined
|
|
146
|
-
? {
|
|
147
|
-
builtInSkillNames: [],
|
|
148
|
-
userSkillMode: "disabled",
|
|
149
|
-
contextApiScopes: [],
|
|
150
|
-
}
|
|
151
|
-
: resolveCodexAppServerSkillSelection(options.selectionName));
|
|
152
|
-
if (selection.userSkillMode !== "all_enabled" || options.userSkillsRoot === undefined) {
|
|
153
|
-
return undefined;
|
|
154
|
-
}
|
|
155
|
-
if (listUserSkills(options.userSkillsRoot).length === 0) {
|
|
156
|
-
return undefined;
|
|
157
|
-
}
|
|
158
|
-
return {
|
|
159
|
-
cwds: [options.cwd],
|
|
160
|
-
forceReload: true,
|
|
161
|
-
perCwdExtraUserRoots: [
|
|
162
|
-
{
|
|
163
|
-
cwd: options.cwd,
|
|
164
|
-
extraUserRoots: [options.userSkillsRoot],
|
|
165
|
-
},
|
|
166
|
-
],
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
152
|
function createTextInputItem(text) {
|
|
170
153
|
return {
|
|
171
154
|
type: "text",
|
|
@@ -206,6 +189,7 @@ export function resolveCodexAppServerContextRuntimeHint(options) {
|
|
|
206
189
|
return undefined;
|
|
207
190
|
}
|
|
208
191
|
const issued = options.agentContext.issueToken({
|
|
192
|
+
...(options.activityRef === undefined ? {} : { activityRef: options.activityRef }),
|
|
209
193
|
selectionName: options.selectionName,
|
|
210
194
|
scopes: selection.contextApiScopes,
|
|
211
195
|
});
|
|
@@ -218,6 +202,7 @@ export function resolveCodexAppServerContextRuntimeHint(options) {
|
|
|
218
202
|
}),
|
|
219
203
|
sensitiveValues: [issued.token, options.agentContext.baseUrl],
|
|
220
204
|
scopes: selection.contextApiScopes,
|
|
205
|
+
...(issued.invocation_ref === undefined ? {} : { invocation_ref: issued.invocation_ref }),
|
|
221
206
|
};
|
|
222
207
|
}
|
|
223
208
|
//# sourceMappingURL=skills.js.map
|