@xfey/tutti 0.1.68 → 0.1.70
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/dist/chat-assistant/index.d.ts +1 -1
- package/dist/chat-assistant/index.js +1 -1
- package/dist/collaboration-state/task-compile-context.js +22 -5
- package/dist/control-plane/index.d.ts +2 -2
- package/dist/control-plane/index.js +30 -2
- package/dist/control-plane/reference-summary-refresh.d.ts +2 -0
- package/dist/control-plane/reference-summary-refresh.js +4 -0
- package/dist/providers/openai/app-server/json-rpc.js +13 -1
- package/dist/providers/openai/app-server/linux-sandbox-readiness.js +6 -3
- package/dist/providers/openai/app-server/read-only-procedure.js +2 -2
- package/dist/providers/openai/app-server/smoke.js +2 -2
- package/dist/providers/openai/app-server/workspace-write-run.js +2 -2
- package/dist/providers/openai/codex-app-server.d.ts +10 -0
- package/dist/providers/openai/codex-app-server.js +22 -5
- package/dist/server-shell/cli/host-server-runtime.js +1 -1
- package/dist/server-shell/http/routes/project-api/messages-routes.js +1 -1
- package/dist/server-shell/http/routes/project-api/reference-files-routes.js +1 -1
- package/dist/server-shell/local-console/invocation-context.d.ts +2 -0
- package/dist/server-shell/local-console/invocation-context.js +10 -1
- package/package.json +1 -1
- package/prompts/procedures/README.md +3 -3
- package/prompts/procedures/follow-up-check.md +6 -4
- package/prompts/procedures/reference-file-summary.md +4 -4
- package/prompts/procedures/task-compile.md +3 -3
- package/prompts/runs/README.md +2 -2
- package/prompts/runs/task-continuation.md +2 -2
- package/prompts/runs/task-retry.md +1 -1
- package/prompts/runs/task-run.md +1 -1
- package/web/assets/{homepage-motion-scene-D2GZ8Nix.js → homepage-motion-scene-Dd2mciax.js} +1 -1
- package/web/assets/{index-NsxRK6_d.js → index-CWKyFntK.js} +2 -2
- package/web/index.html +1 -1
|
@@ -43,7 +43,7 @@ export type ReadOnlyChatAssistantOptions = {
|
|
|
43
43
|
workspaceRoot: string;
|
|
44
44
|
model: ReadOnlyChatAssistantModel;
|
|
45
45
|
promptsRoot?: string;
|
|
46
|
-
onScratchpadSourceChanged?: () => void;
|
|
46
|
+
onScratchpadSourceChanged?: (message: MessageProjection) => void;
|
|
47
47
|
now?: () => Date;
|
|
48
48
|
};
|
|
49
49
|
export declare function isChatAssistantOutput(value: unknown): value is ChatAssistantOutput;
|
|
@@ -206,7 +206,7 @@ export class ReadOnlyChatAssistant {
|
|
|
206
206
|
invalidates: mainChatMessageInvalidates(),
|
|
207
207
|
});
|
|
208
208
|
if (refs?.scratchpad_source !== "exclude") {
|
|
209
|
-
this.options.onScratchpadSourceChanged?.();
|
|
209
|
+
this.options.onScratchpadSourceChanged?.(message);
|
|
210
210
|
}
|
|
211
211
|
}
|
|
212
212
|
appendClarificationRoundAssistantMessage(input) {
|
|
@@ -2,6 +2,16 @@ import { withHostStoreTransaction } from "../store/index.js";
|
|
|
2
2
|
import { readScratchpadProjection } from "./scratchpad.js";
|
|
3
3
|
import { readScratchpadSourceState } from "./scratchpad-source-state.js";
|
|
4
4
|
import { nowIso, stringifyJson } from "./serialization.js";
|
|
5
|
+
function cursorIsAfter(left, right) {
|
|
6
|
+
if (left === undefined) {
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
if (right === undefined) {
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
const timeOrder = left.created_at.localeCompare(right.created_at);
|
|
13
|
+
return timeOrder > 0 || (timeOrder === 0 && left.message_id > right.message_id);
|
|
14
|
+
}
|
|
5
15
|
function parseStringArray(value, field) {
|
|
6
16
|
let parsed;
|
|
7
17
|
try {
|
|
@@ -238,11 +248,18 @@ export function rollbackTaskCompileContext(db, input) {
|
|
|
238
248
|
}
|
|
239
249
|
const rolledBackAt = nowIso(input.now);
|
|
240
250
|
const sourceState = readScratchpadSourceState(tx);
|
|
241
|
-
const dirtySince = sourceState.in_flight !== undefined &&
|
|
242
|
-
new Date(rolledBackAt).getTime() <= new Date(sourceState.in_flight.started_at).getTime()
|
|
243
|
-
? new Date(new Date(sourceState.in_flight.started_at).getTime() + 1).toISOString()
|
|
244
|
-
: rolledBackAt;
|
|
245
251
|
const after = context.source_window.after;
|
|
252
|
+
const through = context.source_window.through;
|
|
253
|
+
const hasBoundaryAfterSourceChanges = sourceState.dirty_since !== undefined ||
|
|
254
|
+
sourceState.in_flight !== undefined ||
|
|
255
|
+
cursorIsAfter(sourceState.refreshed_through_cursor, through);
|
|
256
|
+
const dirtySince = hasBoundaryAfterSourceChanges
|
|
257
|
+
? sourceState.in_flight !== undefined &&
|
|
258
|
+
new Date(rolledBackAt).getTime() <= new Date(sourceState.in_flight.started_at).getTime()
|
|
259
|
+
? new Date(new Date(sourceState.in_flight.started_at).getTime() + 1).toISOString()
|
|
260
|
+
: rolledBackAt
|
|
261
|
+
: null;
|
|
262
|
+
const restoredThrough = hasBoundaryAfterSourceChanges ? after : through;
|
|
246
263
|
tx.prepare(`
|
|
247
264
|
INSERT INTO scratchpad_source_state (
|
|
248
265
|
id,
|
|
@@ -261,7 +278,7 @@ export function rollbackTaskCompileContext(db, input) {
|
|
|
261
278
|
refreshed_through_cursor_message_id = excluded.refreshed_through_cursor_message_id,
|
|
262
279
|
dirty_since = excluded.dirty_since,
|
|
263
280
|
updated_at = excluded.updated_at
|
|
264
|
-
`).run(after?.created_at ?? null, after?.message_id ?? null,
|
|
281
|
+
`).run(after?.created_at ?? null, after?.message_id ?? null, restoredThrough?.created_at ?? null, restoredThrough?.message_id ?? null, dirtySince, rolledBackAt);
|
|
265
282
|
tx.prepare("DELETE FROM active_task_compile_context WHERE workflow_ref = ?").run(input.workflow_ref);
|
|
266
283
|
return true;
|
|
267
284
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type ActivityRef, type ClarificationRoundRef, type IdempotencyKey, type WorkflowInvocationRef } from "@tutti/shared/ids";
|
|
2
|
-
import type { ExecutionStatusProjection, ContextSyncDisposition, ContextSyncResult, RefreshScratchpadDisposition, RefreshScratchpadResult, RunSchedulerNowDisposition, RunSchedulerNowPayload, RefreshReferenceSummariesDisposition, RefreshReferenceSummariesPayload, RefreshReferenceSummariesResult, SendClarificationRoundMessageDisposition, SendClarificationRoundMessagePayload, SendClarificationRoundMessageResult, SubmitClarificationRoundDisposition, SubmitClarificationRoundPayload, SubmitClarificationRoundResult, SubmitWorklistDisposition, SubmitWorklistPayload, SubmitWorklistResult, ProcedureLane } from "@tutti/shared/schemas/api";
|
|
2
|
+
import type { ExecutionStatusProjection, MessageProjection, ContextSyncDisposition, ContextSyncResult, RefreshScratchpadDisposition, RefreshScratchpadResult, RunSchedulerNowDisposition, RunSchedulerNowPayload, RefreshReferenceSummariesDisposition, RefreshReferenceSummariesPayload, RefreshReferenceSummariesResult, SendClarificationRoundMessageDisposition, SendClarificationRoundMessagePayload, SendClarificationRoundMessageResult, SubmitClarificationRoundDisposition, SubmitClarificationRoundPayload, SubmitClarificationRoundResult, SubmitWorklistDisposition, SubmitWorklistPayload, SubmitWorklistResult, ProcedureLane } from "@tutti/shared/schemas/api";
|
|
3
3
|
import type { ContextSyncWorkflowTrigger } from "./workflows/index.js";
|
|
4
4
|
import type { ControlPlaneCommandResult, Phase5ControlPlaneOptions, StartupRecoveryOptions, StartupRecoveryResult, TrustedHumanAuthor } from "./types.js";
|
|
5
5
|
import { type ProjectBriefRefreshReason, type RefreshProjectBriefProjectionResult } from "./project-brief-refresh.js";
|
|
@@ -30,7 +30,7 @@ export declare class Phase5ControlPlane {
|
|
|
30
30
|
startScratchpadRefresh(options?: {
|
|
31
31
|
lane?: ProcedureLane;
|
|
32
32
|
}): ControlPlaneCommandResult<RefreshScratchpadDisposition, RefreshScratchpadResult>;
|
|
33
|
-
notifyScratchpadSourceChanged(): void;
|
|
33
|
+
notifyScratchpadSourceChanged(message: MessageProjection): void;
|
|
34
34
|
refreshProjectBrief(options: {
|
|
35
35
|
reason: ProjectBriefRefreshReason;
|
|
36
36
|
workflowRef?: WorkflowInvocationRef;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readActiveTaskCompileContext, readWorklistProjection, } from "../collaboration-state/index.js";
|
|
2
2
|
import { ProcedureEngine } from "../procedure-engine/index.js";
|
|
3
3
|
import { RunEngine } from "../run-engine/index.js";
|
|
4
|
+
import { WorkspaceOpsError, readReferenceSummaryTargets, } from "../workspace-ops/index.js";
|
|
4
5
|
import { publishProcedureTransition as publishProcedureTransitionEvent, publishRunResultRecorded as publishRunResultRecordedEvent, publishRunTransition as publishRunTransitionEvent, publishTaskRunStarted as publishTaskRunStartedEvent, } from "./event-publishers.js";
|
|
5
6
|
import { startContextSyncAnalysisProcedure } from "./context-sync.js";
|
|
6
7
|
import { refreshProjectBriefProjection, } from "./project-brief-refresh.js";
|
|
@@ -9,6 +10,7 @@ import { runControlPlaneStartupRecovery } from "./startup-recovery.js";
|
|
|
9
10
|
import { readControlPlaneExecutionStatus } from "./execution-status.js";
|
|
10
11
|
import { ScratchpadAutoRefreshScheduler } from "./scratchpad-auto-refresh.js";
|
|
11
12
|
import { startControlPlaneScratchpadRefresh } from "./scratchpad-refresh-start.js";
|
|
13
|
+
import { shouldUseMessageAsScratchpadSource } from "./scratchpad-source-messages.js";
|
|
12
14
|
import { startControlPlaneTaskCompile } from "./task-compile-start.js";
|
|
13
15
|
import { startControlPlaneTaskCompileContinuation } from "./task-compile-continuation.js";
|
|
14
16
|
import { startControlPlaneTaskBoundFollowUpCheck } from "./follow-up-start.js";
|
|
@@ -106,7 +108,10 @@ export class Phase5ControlPlane {
|
|
|
106
108
|
publishProcedureTransition: (state) => this.publishProcedureTransition(state),
|
|
107
109
|
});
|
|
108
110
|
}
|
|
109
|
-
notifyScratchpadSourceChanged() {
|
|
111
|
+
notifyScratchpadSourceChanged(message) {
|
|
112
|
+
if (!shouldUseMessageAsScratchpadSource(message)) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
110
115
|
this.scratchpadAutoRefreshScheduler.notifySourceChanged();
|
|
111
116
|
}
|
|
112
117
|
refreshProjectBrief(options) {
|
|
@@ -169,8 +174,26 @@ export class Phase5ControlPlane {
|
|
|
169
174
|
}
|
|
170
175
|
startReferenceSummaryRefresh(payload = {}) {
|
|
171
176
|
if (payload.paths === undefined) {
|
|
172
|
-
this.pendingAllReferenceSummaries =
|
|
177
|
+
this.pendingAllReferenceSummaries = false;
|
|
173
178
|
this.pendingReferenceSummaryPaths.clear();
|
|
179
|
+
if (this.projectContext === undefined) {
|
|
180
|
+
this.pendingAllReferenceSummaries = true;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
try {
|
|
184
|
+
for (const target of readReferenceSummaryTargets({
|
|
185
|
+
workspaceRoot: this.projectContext.workspaceRoot,
|
|
186
|
+
now: this.now,
|
|
187
|
+
})) {
|
|
188
|
+
this.pendingReferenceSummaryPaths.add(target.path);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (!(error instanceof WorkspaceOpsError && error.code === "not_found")) {
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
174
197
|
}
|
|
175
198
|
else {
|
|
176
199
|
for (const path of payload.paths) {
|
|
@@ -192,6 +215,11 @@ export class Phase5ControlPlane {
|
|
|
192
215
|
projectContext: this.projectContext,
|
|
193
216
|
logger: this.logger,
|
|
194
217
|
now: this.now,
|
|
218
|
+
onTargetsPersisted: (targets) => {
|
|
219
|
+
for (const target of targets) {
|
|
220
|
+
this.pendingReferenceSummaryPaths.delete(target.path);
|
|
221
|
+
}
|
|
222
|
+
},
|
|
195
223
|
publishProcedureTransition: (state) => this.publishProcedureTransition(state),
|
|
196
224
|
});
|
|
197
225
|
if (result.disposition.kind !== "already_running") {
|
|
@@ -3,6 +3,7 @@ import type { RefreshReferenceSummariesDisposition, RefreshReferenceSummariesRes
|
|
|
3
3
|
import type { ProcedureEngine, ProcedureEngineState } from "../procedure-engine/index.js";
|
|
4
4
|
import type { WorkspaceEventBus } from "../server-shell/http/workspace-events.js";
|
|
5
5
|
import type { HostProjectStore } from "../store/index.js";
|
|
6
|
+
import { type ReferenceSummaryTarget } from "../workspace-ops/index.js";
|
|
6
7
|
import type { ControlPlaneCommandResult, ControlPlaneLogger, Phase5ControlPlaneOptions } from "./types.js";
|
|
7
8
|
import type { ProcedureWorkflowRunnerResolver } from "./workflows/index.js";
|
|
8
9
|
export declare function startReferenceSummaryRefreshProcedure(input: {
|
|
@@ -21,6 +22,7 @@ export declare function startReferenceSummaryRefreshProcedure(input: {
|
|
|
21
22
|
activityRef: ActivityRef;
|
|
22
23
|
workflowRef: WorkflowInvocationRef;
|
|
23
24
|
}) => void;
|
|
25
|
+
onTargetsPersisted?: (targets: ReferenceSummaryTarget[]) => void;
|
|
24
26
|
publishProcedureTransition: (state: ProcedureEngineState) => void;
|
|
25
27
|
}): ControlPlaneCommandResult<RefreshReferenceSummariesDisposition, RefreshReferenceSummariesResult>;
|
|
26
28
|
//# sourceMappingURL=reference-summary-refresh.d.ts.map
|
|
@@ -135,6 +135,7 @@ async function runReferenceSummaryRefresh(options) {
|
|
|
135
135
|
if (result.commit_oid !== undefined) {
|
|
136
136
|
lastCommitOid = result.commit_oid;
|
|
137
137
|
}
|
|
138
|
+
options.onTargetsPersisted?.(batch);
|
|
138
139
|
targets = readTargets({
|
|
139
140
|
projectContext: options.projectContext,
|
|
140
141
|
now: options.now,
|
|
@@ -379,6 +380,9 @@ export function startReferenceSummaryRefreshProcedure(input) {
|
|
|
379
380
|
events: input.events,
|
|
380
381
|
logger: input.logger,
|
|
381
382
|
reportProgress: context.reportProgress,
|
|
383
|
+
...(input.onTargetsPersisted === undefined
|
|
384
|
+
? {}
|
|
385
|
+
: { onTargetsPersisted: input.onTargetsPersisted }),
|
|
382
386
|
now: input.now,
|
|
383
387
|
});
|
|
384
388
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { StringDecoder } from "node:string_decoder";
|
|
2
2
|
import { redactText } from "@tutti/shared/utils";
|
|
3
|
+
const PROCESS_EXIT_STDERR_EXCERPT_MAX_LENGTH = 500;
|
|
3
4
|
export class CodexAppServerProtocolError extends Error {
|
|
4
5
|
code;
|
|
5
6
|
constructor(message, code) {
|
|
@@ -21,6 +22,16 @@ function normalizeProtocolError(method, error) {
|
|
|
21
22
|
const message = typeof rawMessage === "string" ? redactText(rawMessage) : "protocol_error";
|
|
22
23
|
return new CodexAppServerProtocolError(`Codex app-server request failed: ${method}: ${code}: ${message}`, "request_failed");
|
|
23
24
|
}
|
|
25
|
+
function processExitStderrExcerpt(value) {
|
|
26
|
+
const normalized = redactText(value).replace(/\s+/gu, " ").trim();
|
|
27
|
+
if (normalized.length === 0) {
|
|
28
|
+
return "";
|
|
29
|
+
}
|
|
30
|
+
if (normalized.length <= PROCESS_EXIT_STDERR_EXCERPT_MAX_LENGTH) {
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
return `${normalized.slice(0, PROCESS_EXIT_STDERR_EXCERPT_MAX_LENGTH)}...`;
|
|
34
|
+
}
|
|
24
35
|
export class CodexAppServerJsonRpcClient {
|
|
25
36
|
child;
|
|
26
37
|
options;
|
|
@@ -47,7 +58,8 @@ export class CodexAppServerJsonRpcClient {
|
|
|
47
58
|
});
|
|
48
59
|
child.on("exit", (code, signal) => {
|
|
49
60
|
this.closed = true;
|
|
50
|
-
|
|
61
|
+
const stderrExcerpt = processExitStderrExcerpt(this.stderrText);
|
|
62
|
+
this.rejectAll(new CodexAppServerProtocolError(`Codex app-server exited before request completed: code=${code ?? "null"} signal=${signal ?? "null"}${stderrExcerpt === "" ? "" : ` stderr=${stderrExcerpt}`}`, "process_exited"));
|
|
51
63
|
});
|
|
52
64
|
child.on("error", (error) => {
|
|
53
65
|
this.closed = true;
|
|
@@ -2,7 +2,7 @@ import { spawnSync } from "node:child_process";
|
|
|
2
2
|
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
3
|
import { tmpdir } from "node:os";
|
|
4
4
|
import { join } from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { buildCodexCliProcessPlan } from "../codex-app-server.js";
|
|
6
6
|
import { createCodexSpawnEnv } from "./runtime-helpers.js";
|
|
7
7
|
const DEFAULT_PROBE_TIMEOUT_MS = 10_000;
|
|
8
8
|
export const CODEX_LINUX_SANDBOX_GUIDANCE = "Install bubblewrap. On Ubuntu 24.04, also install apparmor-profiles and apparmor-utils and load the distribution bwrap-userns-restrict profile. Then run `tutti doctor` again.";
|
|
@@ -74,9 +74,12 @@ export function inspectCodexLinuxSandboxReadiness(options = {}) {
|
|
|
74
74
|
const codexHome = join(tempRoot, "codex-home");
|
|
75
75
|
mkdirSync(codexHome, { recursive: true, mode: 0o700 });
|
|
76
76
|
const runProbe = options.runProbe ?? defaultProbeRunner;
|
|
77
|
-
|
|
78
|
-
command: options.codexCommand ?? CODEX_APP_SERVER_COMMAND,
|
|
77
|
+
const processPlan = buildCodexCliProcessPlan({
|
|
79
78
|
args: ["sandbox", "--", "/bin/true"],
|
|
79
|
+
...(options.codexCommand === undefined ? {} : { codexCommand: options.codexCommand }),
|
|
80
|
+
});
|
|
81
|
+
return classifyProbeResult(runProbe({
|
|
82
|
+
...processPlan,
|
|
80
83
|
cwd: tempRoot,
|
|
81
84
|
env: createCodexSpawnEnv(codexHome, options.baseEnv ?? process.env),
|
|
82
85
|
timeoutMs: options.timeoutMs ?? DEFAULT_PROBE_TIMEOUT_MS,
|
|
@@ -120,9 +120,9 @@ export async function runCodexAppServerReadOnlyProcedure(options) {
|
|
|
120
120
|
const processPlan = buildCodexAppServerProcessPlan({
|
|
121
121
|
codexHome,
|
|
122
122
|
configOverrides: buildCodexAppServerProviderConfigOverrides(options),
|
|
123
|
+
...(options.codexCommand === undefined ? {} : { codexCommand: options.codexCommand }),
|
|
123
124
|
});
|
|
124
|
-
const
|
|
125
|
-
const child = spawn(command, processPlan.args, {
|
|
125
|
+
const child = spawn(processPlan.command, processPlan.args, {
|
|
126
126
|
cwd: process.cwd(),
|
|
127
127
|
env: createCodexSpawnEnv(codexHome, options.baseEnv ?? process.env),
|
|
128
128
|
stdio: "pipe",
|
|
@@ -25,9 +25,9 @@ export async function runCodexAppServerNoWriteSmoke(options) {
|
|
|
25
25
|
const processPlan = buildCodexAppServerProcessPlan({
|
|
26
26
|
codexHome,
|
|
27
27
|
configOverrides: buildCodexAppServerProviderConfigOverrides(options),
|
|
28
|
+
...(options.codexCommand === undefined ? {} : { codexCommand: options.codexCommand }),
|
|
28
29
|
});
|
|
29
|
-
const
|
|
30
|
-
const child = spawn(command, processPlan.args, {
|
|
30
|
+
const child = spawn(processPlan.command, processPlan.args, {
|
|
31
31
|
cwd: process.cwd(),
|
|
32
32
|
env: createCodexSpawnEnv(codexHome, options.baseEnv ?? process.env),
|
|
33
33
|
stdio: "pipe",
|
|
@@ -196,9 +196,9 @@ export async function runCodexAppServerWorkspaceWriteRun(options) {
|
|
|
196
196
|
const processPlan = buildCodexAppServerProcessPlan({
|
|
197
197
|
codexHome,
|
|
198
198
|
configOverrides: buildCodexAppServerProviderConfigOverrides(options),
|
|
199
|
+
...(options.codexCommand === undefined ? {} : { codexCommand: options.codexCommand }),
|
|
199
200
|
});
|
|
200
|
-
const
|
|
201
|
-
const child = spawn(command, processPlan.args, {
|
|
201
|
+
const child = spawn(processPlan.command, processPlan.args, {
|
|
202
202
|
cwd: process.cwd(),
|
|
203
203
|
env: createCodexSpawnEnv(codexHome, options.baseEnv ?? process.env),
|
|
204
204
|
stdio: "pipe",
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export declare const CODEX_APP_SERVER_ENTRYPOINT: string;
|
|
1
2
|
export declare const CODEX_APP_SERVER_COMMAND: string;
|
|
2
3
|
export declare const CODEX_APP_SERVER_LISTEN_URL: "stdio://";
|
|
3
4
|
export declare const CODEX_APP_SERVER_CLIENT_NAME: "tutti_host";
|
|
@@ -73,8 +74,13 @@ export type CodexAppServerTurnStartTemplatePlan = {
|
|
|
73
74
|
};
|
|
74
75
|
export type BuildCodexAppServerProcessPlanOptions = {
|
|
75
76
|
codexHome: string;
|
|
77
|
+
codexCommand?: string;
|
|
76
78
|
configOverrides?: readonly CodexAppServerConfigOverride[];
|
|
77
79
|
};
|
|
80
|
+
export type CodexCliProcessPlan = {
|
|
81
|
+
command: string;
|
|
82
|
+
args: string[];
|
|
83
|
+
};
|
|
78
84
|
export type CodexAppServerNoWriteSmokePlan = {
|
|
79
85
|
process: CodexAppServerProcessPlan;
|
|
80
86
|
initialize: CodexAppServerRequestPlan<"initialize", CodexAppServerInitializeRequestParams>;
|
|
@@ -107,6 +113,10 @@ export declare const CODEX_NO_WRITE_SMOKE_OUTPUT_SCHEMA: {
|
|
|
107
113
|
export declare function createCodexAppServerProcessEnv(options: {
|
|
108
114
|
codexHome: string;
|
|
109
115
|
}): Record<string, string>;
|
|
116
|
+
export declare function buildCodexCliProcessPlan(options: {
|
|
117
|
+
args: readonly string[];
|
|
118
|
+
codexCommand?: string;
|
|
119
|
+
}): CodexCliProcessPlan;
|
|
110
120
|
export declare function buildCodexAppServerProcessPlan(options: BuildCodexAppServerProcessPlanOptions): CodexAppServerProcessPlan;
|
|
111
121
|
export declare function createCodexAppServerNoWriteSmokePlan(options: CodexNoWriteSmokePlanOptions): CodexAppServerNoWriteSmokePlan;
|
|
112
122
|
export declare class CodexAppServerJsonlParseError extends Error {
|
|
@@ -2,7 +2,8 @@ import { createRequire } from "node:module";
|
|
|
2
2
|
import { dirname, isAbsolute, join } from "node:path";
|
|
3
3
|
import { DEFAULT_OPENAI_MODEL } from "./model-config.js";
|
|
4
4
|
const require = createRequire(import.meta.url);
|
|
5
|
-
export const
|
|
5
|
+
export const CODEX_APP_SERVER_ENTRYPOINT = join(dirname(require.resolve("@openai/codex/package.json")), "bin", "codex.js");
|
|
6
|
+
export const CODEX_APP_SERVER_COMMAND = process.execPath;
|
|
6
7
|
export const CODEX_APP_SERVER_LISTEN_URL = "stdio://";
|
|
7
8
|
export const CODEX_APP_SERVER_CLIENT_NAME = "tutti_host";
|
|
8
9
|
export const CODEX_APP_SERVER_CLIENT_TITLE = "Tutti Host";
|
|
@@ -37,12 +38,28 @@ export function createCodexAppServerProcessEnv(options) {
|
|
|
37
38
|
CODEX_HOME: options.codexHome,
|
|
38
39
|
};
|
|
39
40
|
}
|
|
40
|
-
export function
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
export function buildCodexCliProcessPlan(options) {
|
|
42
|
+
if (options.codexCommand !== undefined) {
|
|
43
|
+
assertNonEmpty(options.codexCommand, "codexCommand");
|
|
44
|
+
return {
|
|
45
|
+
command: options.codexCommand,
|
|
46
|
+
args: [...options.args],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
43
49
|
return {
|
|
44
50
|
command: CODEX_APP_SERVER_COMMAND,
|
|
45
|
-
args,
|
|
51
|
+
args: [CODEX_APP_SERVER_ENTRYPOINT, ...options.args],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
export function buildCodexAppServerProcessPlan(options) {
|
|
55
|
+
const codexArgs = ["app-server", "--listen", CODEX_APP_SERVER_LISTEN_URL];
|
|
56
|
+
pushConfigOverrides(codexArgs, options.configOverrides ?? []);
|
|
57
|
+
const processPlan = buildCodexCliProcessPlan({
|
|
58
|
+
args: codexArgs,
|
|
59
|
+
...(options.codexCommand === undefined ? {} : { codexCommand: options.codexCommand }),
|
|
60
|
+
});
|
|
61
|
+
return {
|
|
62
|
+
...processPlan,
|
|
46
63
|
env: createCodexAppServerProcessEnv({ codexHome: options.codexHome }),
|
|
47
64
|
transport: {
|
|
48
65
|
kind: "stdio",
|
|
@@ -283,7 +283,7 @@ export async function startForegroundHostServer(options) {
|
|
|
283
283
|
return await resolution.model.answer(input);
|
|
284
284
|
},
|
|
285
285
|
},
|
|
286
|
-
onScratchpadSourceChanged: () => controlPlane.notifyScratchpadSourceChanged(),
|
|
286
|
+
onScratchpadSourceChanged: (message) => controlPlane.notifyScratchpadSourceChanged(message),
|
|
287
287
|
now,
|
|
288
288
|
});
|
|
289
289
|
let close = async () => { };
|
|
@@ -71,7 +71,7 @@ export function registerMessagesRoutes(app, { options, workspaceEvents, }) {
|
|
|
71
71
|
},
|
|
72
72
|
invalidates: mainChatMessageInvalidates(),
|
|
73
73
|
});
|
|
74
|
-
options.controlPlane?.notifyScratchpadSourceChanged();
|
|
74
|
+
options.controlPlane?.notifyScratchpadSourceChanged(execution.result.message);
|
|
75
75
|
options.chatAssistant?.enqueue({ message: execution.result.message });
|
|
76
76
|
}
|
|
77
77
|
return response;
|
|
@@ -137,7 +137,7 @@ export function registerReferenceFilesRoutes(app, { options, workspaceEvents, })
|
|
|
137
137
|
},
|
|
138
138
|
invalidates,
|
|
139
139
|
});
|
|
140
|
-
options.controlPlane?.notifyScratchpadSourceChanged();
|
|
140
|
+
options.controlPlane?.notifyScratchpadSourceChanged(execution.result.message);
|
|
141
141
|
options.controlPlane?.startReferenceSummaryRefresh({
|
|
142
142
|
paths: [execution.result.file.path],
|
|
143
143
|
});
|
|
@@ -19,10 +19,12 @@ export declare function createDesktopLocalConsoleInvocationContext(options: {
|
|
|
19
19
|
export declare function createLocalConsoleServiceEnvironment(options: {
|
|
20
20
|
env: NodeJS.ProcessEnv;
|
|
21
21
|
tuttiHome: string;
|
|
22
|
+
runtimeExecutablePath?: string;
|
|
22
23
|
}): NodeJS.ProcessEnv;
|
|
23
24
|
export declare function createLocalConsoleOperationEnvironment(options: {
|
|
24
25
|
serviceEnvironment: NodeJS.ProcessEnv;
|
|
25
26
|
invocationEnvironment: LocalConsoleInvocationEnvironment;
|
|
26
27
|
tuttiHome: string;
|
|
28
|
+
runtimeExecutablePath?: string;
|
|
27
29
|
}): NodeJS.ProcessEnv;
|
|
28
30
|
//# sourceMappingURL=invocation-context.d.ts.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { resolve } from "node:path";
|
|
1
|
+
import { delimiter, dirname, resolve } from "node:path";
|
|
2
2
|
export const LOCAL_CONSOLE_INVOCATION_ENV_KEYS = [
|
|
3
3
|
"DBUS_SESSION_BUS_ADDRESS",
|
|
4
4
|
"DISPLAY",
|
|
@@ -12,6 +12,12 @@ export const LOCAL_CONSOLE_INVOCATION_ENV_KEYS = [
|
|
|
12
12
|
"WAYLAND_DISPLAY",
|
|
13
13
|
"XDG_CURRENT_DESKTOP",
|
|
14
14
|
];
|
|
15
|
+
function mergeExecutablePaths(values, runtimeExecutablePath) {
|
|
16
|
+
const entries = [dirname(runtimeExecutablePath), ...values.flatMap((value) => value?.split(delimiter) ?? [])]
|
|
17
|
+
.map((entry) => entry.trim())
|
|
18
|
+
.filter((entry) => entry !== "");
|
|
19
|
+
return [...new Set(entries)].join(delimiter);
|
|
20
|
+
}
|
|
15
21
|
export function collectLocalConsoleInvocationEnvironment(env) {
|
|
16
22
|
const environment = {};
|
|
17
23
|
for (const key of LOCAL_CONSOLE_INVOCATION_ENV_KEYS) {
|
|
@@ -52,12 +58,15 @@ export function createLocalConsoleServiceEnvironment(options) {
|
|
|
52
58
|
environment[key] = value;
|
|
53
59
|
}
|
|
54
60
|
}
|
|
61
|
+
environment.PATH = mergeExecutablePaths([environment.PATH], options.runtimeExecutablePath ?? process.execPath);
|
|
55
62
|
return environment;
|
|
56
63
|
}
|
|
57
64
|
export function createLocalConsoleOperationEnvironment(options) {
|
|
65
|
+
const executablePath = mergeExecutablePaths([options.invocationEnvironment.PATH, options.serviceEnvironment.PATH], options.runtimeExecutablePath ?? process.execPath);
|
|
58
66
|
return {
|
|
59
67
|
...options.serviceEnvironment,
|
|
60
68
|
...options.invocationEnvironment,
|
|
69
|
+
PATH: executablePath,
|
|
61
70
|
TUTTI_HOME: options.tuttiHome,
|
|
62
71
|
};
|
|
63
72
|
}
|
package/package.json
CHANGED
|
@@ -9,19 +9,19 @@
|
|
|
9
9
|
- `task-compile.md`:使用 Codex app-server read-only adapter,根据直接输入的 Scratchpad / Worklist 与按需读取的项目上下文文件生成追加式 task compile proposal,或请求 workflow-bound clarification;执行面仅为 app-server read-only。
|
|
10
10
|
- `project-context-bootstrap.md`:使用 Codex app-server read-only adapter 为缺失 baseline context files 生成完整文件内容计划;实际写入和 commit 由 `workspace-ops` 受控完成。
|
|
11
11
|
- `context-sync.md`:使用 Codex app-server read-only adapter 生成文档同步更新计划;实际文件写入和 commit 由 `workspace-ops` 受控完成。
|
|
12
|
-
- `reference-file-summary.md`:使用 Codex app-server read-only adapter 读取单个 reference
|
|
12
|
+
- `reference-file-summary.md`:使用 Codex app-server read-only adapter 读取单个 reference 文件并生成“包含什么”的抽象 summary;不复现原句、表格行、个人信息、标识符或 secret-like value,正文敏感本身不构成 unavailable,只有无法访问、解码或理解才返回该分支。实际 README summary 索引写入和 commit 由 `workspace-ops` 受控完成。
|
|
13
13
|
- `follow-up-check.md`:在 task-bound clarification submit 后判断同一 task 契约能否继续,返回 `resume` / `needs_more_input` / `requires_retasking`。
|
|
14
14
|
|
|
15
15
|
`project_context_bootstrap` 的 provider path 只让 Codex app-server read-only adapter 生成缺失文档内容;如果该计划不可用,Control Plane 会回退到本地保守模板。
|
|
16
16
|
`project_context_bootstrap` 的 read-only 输入只包含项目名、非空时的 `missing_docs` 与刷新后的 Scratchpad;prompt 会要求 app-server 只读探索仓库,并可通过 `read-project-docs` / `read-references` skill 按需读取现有文档和参考文件。输出只包含 `documents[].doc_key/content`,固定 path 由 Control Plane 后处理生成。
|
|
17
|
-
`task_compile` 的 read-only 输入只包含 durable context 固定的五字段 Scratchpad、当前 Worklist、可选 compact `recent_references` 和可选完整有序 `clarification_rounds`;reference 项只含 path、最终 name 与 available/unavailable summary,不含 blob/message/workflow audit fields。每次 continuation 仍读取同一 pinned Scratchpad 与 source set,不读取并行变化的 Current Scratchpad或 boundary 后的新上传;later round 可以纠正 earlier round,明确撤回时允许空 proposal 正常结束。prompt 暴露 `read-project-docs` / `read-worklist` / `read-references` skills 供 app-server 必要时读取项目上下文文件、既有任务 contract / latest result 或相关 reference
|
|
17
|
+
`task_compile` 的 read-only 输入只包含 durable context 固定的五字段 Scratchpad、当前 Worklist、可选 compact `recent_references` 和可选完整有序 `clarification_rounds`;reference 项只含 path、最终 name 与 available/unavailable summary,不含 blob/message/workflow audit fields。每次 continuation 仍读取同一 pinned Scratchpad 与 source set,不读取并行变化的 Current Scratchpad或 boundary 后的新上传;later round 可以纠正 earlier round,明确撤回时允许空 proposal 正常结束。prompt 暴露 `read-project-docs` / `read-worklist` / `read-references` skills 供 app-server 必要时读取项目上下文文件、既有任务 contract / latest result 或相关 reference 正文;拟议工作实质依赖 reference 时必须在冻结 contract 前读取 source,不以 summary 替代原文。只有现有上下文支持一个 coherent formal contract 时才 proposal;重大歧义或冲突若必须替用户选择意图,则请求最少 clarification,低影响、可逆或已有 convention 覆盖的细节继续写成有界假设。Scratchpad 和 Worklist 紧凑视图仍以直接输入为准,不通过 skill 重读 Scratchpad。provider raw output 使用 `{ result: ... }` 互斥分支;proposal 分支用 `module_ref` 指向新模块或既有模块,task 只输出 `title / goal / scope`,runner adapter 再映射为 Control Plane 内部 proposal:`goal` 同时作为内部 task summary 和 contract goal,`scope` 作为内部 contract scope。
|
|
18
18
|
`context_sync` 没有 provider-facing direct input;workflow ref、activity ref、触发原因、安全元数据、checked paths、skipped paths 和 open questions 都不进入 prompt,只用于运行时追踪、调度或审计。prompt 说明通用文档同步目标、目标项目文档落点和 canonical docs 职责,由 app-server 主动探索 repo,并通过 `read-project-docs` / `read-worklist` / `read-references` skill 按需读取现有文档、正式任务上下文和参考文件。Worklist / task result 只作为阶段性完成背景和稳定结果线索,不写成长文档里的逐任务流水。
|
|
19
19
|
`context_sync` prompt 只返回 `summary` 与 `updates[].path/content`;空 `updates` 表示无需同步,非空 `updates` 由 Control Plane 调用 `workspace-ops` 做 path policy、mainline clean 检查、文件写入和 Git commit。
|
|
20
20
|
`project_brief_refresh` 只走 OpenAI SDK structured output,不使用 Codex app-server,不读取 repo 或 skills。它接收四个 baseline Project Docs 的 bounded source 内容与状态,输出 `product_summary / tech_summary / structure_summary / principles_summary`,由 Control Plane 写入 host-local `project_brief` projection;Project Docs 创建、同步、被成功 Run promotion 修改,或 provider setup 后需要 backfill 时可以触发。provider 不可用、source 不可用、brief 已经 fresh 或刷新失败都不影响 Project Docs 写入结果。
|
|
21
21
|
`reference_summary_refresh` 不接收 provider-facing workflow input。Control Plane 内部保留 workflow / activity trace anchors 与单个 reference file metadata 用于定位、审计和写回;runner 会把目标文件复制到一个临时目录,并以该目录作为 app-server `cwd`,prompt 只说明读取当前目录中的唯一目标文件、禁用写入和网络,并返回 bounded structured summary 输出。
|
|
22
22
|
Phase 6.5 的 reference upload message 可以作为普通聊天事实进入 `scratchpad_refresh`,但 prompt 明确禁止模型在未读取文件的情况下推断文件正文。
|
|
23
23
|
`scratchpad_refresh` 与 `task_compile` 在压缩上下文时应保留任务执行和验收必需的用户提供细节。`scratchpad_refresh` 读取当前周期完整、有序的主群聊 `messages`,并按 `messages > active_task_compile > worklist > project_brief` 理解上下文;只有 `messages` 可以产生新的 `task_changes`。每条超长消息先做保留头尾的中间截断,再合并连续作者;不得静默删除较早消息。需要排除的是 secret、provider 细节、raw log 和 Tutti runtime / internal path。
|
|
24
|
-
`follow_up_check` 的 provider-facing 输入只包含展平后的 `title / goal / scope`、上一 `Run.needs_human` 的 `summary`、原 `clarification_request_payload` 和压缩后的 `answers[]`;workflow ref、task id/status、activity ref、result kind、Run lineage、message id、作者 identity 和时间戳不进入 prompt
|
|
24
|
+
`follow_up_check` 的 provider-facing 输入只包含展平后的 `title / goal / scope`、上一 `Run.needs_human` 的 `summary`、原 `clarification_request_payload` 和压缩后的 `answers[]`;workflow ref、task id/status、activity ref、result kind、Run lineage、message id、作者 identity 和时间戳不进入 prompt。它只判断原 human decision 是否在同一 frozen contract 内解决:已解决为 `resume`,同一决策仍可在 contract 内补充为 `needs_more_input`,采纳回答会改变 goal / scope 为 `requires_retasking`;不得请求 Run 自己负责的实现选择。provider raw output 使用 `{ result: ... }` 互斥分支,runner adapter 再映射为 Control Plane 内部 outcome。控制面负责把三个结果分别物化为 continuation、下一轮 round 或 retasking-required closure。`Run.failed` / `Task.failed` 当前不再通过 follow-up retry 复活。
|
|
25
25
|
|
|
26
26
|
## 输入变量
|
|
27
27
|
|
|
@@ -33,11 +33,13 @@ Fields:
|
|
|
33
33
|
# Decision Rules
|
|
34
34
|
|
|
35
35
|
- Use only `workflow_input_json`; do not inspect files, repository state, Worklist, Scratchpad, references, Project Docs, or external context.
|
|
36
|
-
- Treat `goal` and `scope` as the frozen task contract, `previous_run.summary` and `previous_run.clarification_request_payload` as the
|
|
37
|
-
-
|
|
38
|
-
- Return `
|
|
39
|
-
- Return `
|
|
36
|
+
- Treat `goal` and `scope` as the frozen task contract, `previous_run.summary` and `previous_run.clarification_request_payload` as the identified human decision, and `answers` as the submitted human reply.
|
|
37
|
+
- Evaluate only whether the answers resolve that decision within the same frozen task contract.
|
|
38
|
+
- Return `resume` when the blocker is resolved without changing the contract.
|
|
39
|
+
- Return `needs_more_input` when the same human decision remains unresolved but can still be answered within the contract.
|
|
40
|
+
- Return `requires_retasking` when honoring the answers would change the task's goal or scope.
|
|
40
41
|
- Treat user-provided non-secret details in answers as valid continuation context when they resolve the blocker.
|
|
42
|
+
- Do not request decisions about implementation choices that remain the Run's responsibility.
|
|
41
43
|
- Do not patch the task contract, create tasks, suggest Worklist edits, or ask for credentials, secrets, deployment tokens, cookies, or Tutti runtime/internal paths.
|
|
42
44
|
|
|
43
45
|
# Output
|
|
@@ -18,10 +18,10 @@ You summarize one human-uploaded Tutti reference file.
|
|
|
18
18
|
# Task
|
|
19
19
|
|
|
20
20
|
- Read only the single target file in the current directory, and do not inspect parent directories or unrelated files.
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
24
|
-
-
|
|
21
|
+
- Describe what the file contains in one concise sentence under 400 characters, based on content you can access and understand rather than guessed metadata.
|
|
22
|
+
- Keep the description abstract: identify the subject, information types, notable structure, and apparent purpose without reproducing source sentences, rows, personal details, identifiers, or secret-like values.
|
|
23
|
+
- If the file contains sensitive material, describe only its general category and purpose without exposing values or quoting the content. Content sensitivity alone does not make a summary unavailable.
|
|
24
|
+
- Return `unavailable` with a brief natural-language reason only if the file cannot be accessed, decoded, or understood well enough to describe what it contains.
|
|
25
25
|
- Do not edit files, stage files, commit, install dependencies, run builds, run tests, start dev servers, deploy, perform network lookups, or include local absolute paths, provider details, host paths, cookies, or tokens.
|
|
26
26
|
|
|
27
27
|
# Output
|
|
@@ -27,7 +27,7 @@ You compile Tutti's Scratchpad into an append-only Worklist proposal for post-ba
|
|
|
27
27
|
- Treat the provided Scratchpad and Worklist as authoritative. Do not use skills to reread them.
|
|
28
28
|
- Use `read-worklist` only when an existing task's contract or latest result would clarify the current task's subject, boundary, or delivery relationship. Do not duplicate tasks already covered by the Worklist.
|
|
29
29
|
- Use `read-project-docs` or `read-references` only when task scope, acceptance, product intent, technical stack, repo structure, agent working rules, or reference contents matter and are not clear from direct input.
|
|
30
|
-
-
|
|
30
|
+
- When proposed work materially depends on a recent reference, read the relevant source before freezing the task contract; do not rely on its summary alone.
|
|
31
31
|
- Preserve user-provided non-secret details when they are needed to execute or accept the task. Do not generalize them into vague labels.
|
|
32
32
|
- Do not use web search, credentials, provider internals, raw logs, or Tutti runtime/internal paths.
|
|
33
33
|
|
|
@@ -44,11 +44,11 @@ When the Scratchpad clearly asks for one of these direct deliverables, include a
|
|
|
44
44
|
|
|
45
45
|
# Readiness
|
|
46
46
|
|
|
47
|
-
Return a `proposal` when
|
|
47
|
+
Return a `proposal` only when the available context supports one coherent formal task contract without making a high-impact choice for the users. Treat an unresolved material ambiguity or conflict as a missing decision when resolving it would require choosing the users' intent; ask the minimum clarification needed instead of turning that choice into an assumption. Missing low-impact details, reversible choices, and established project conventions should usually become bounded assumptions or notes inside task `scope`, not clarification.
|
|
48
48
|
|
|
49
49
|
Return `clarification_request_payload` only when all are true:
|
|
50
50
|
|
|
51
|
-
-
|
|
51
|
+
- Resolving the issue would materially change goal, scope, user-visible behavior, visual or interaction direction, technical shape, data or asset source, acceptance criteria, safety boundary, or external side effect.
|
|
52
52
|
- Scratchpad, Worklist, Project Docs, repo facts, and relevant references do not answer it.
|
|
53
53
|
- Guessing would likely cause rework, risk, or user surprise.
|
|
54
54
|
- A concise human answer can unblock task creation.
|
package/prompts/runs/README.md
CHANGED
|
@@ -7,12 +7,12 @@ These templates are used by task `Run` execution.
|
|
|
7
7
|
- `task-retry.md` is rendered for bounded Run pipeline self-correction retries after feedback such as no candidate diff before or after checks, Artifact validation failure, failed checks, or accepted-checkpoint failure. It is not a failed-task follow-up path and does not resurrect `Task.failed`.
|
|
8
8
|
Templates in this directory may describe task contracts, repo evidence, check expectations, artifact declaration rules, and documentation update responsibilities. They must never include provider credentials or host-local secret material.
|
|
9
9
|
|
|
10
|
-
`task-run.md`
|
|
10
|
+
`task-run.md` keeps implementation judgment inside the frozen contract with Codex. Human clarification is reserved for interpreting contract intent, choosing among incompatible requirements or source facts, supplying required non-secret context, relaxing an explicit constraint, or accepting a material risk or external effect. Technical difficulty, tooling failure, and validation failure stay with the Run when they can be handled; otherwise the result is `failed`, not a question for the user. If the task contract does not require repository file changes, the output can use `completed_no_repo_changes`; otherwise a completed implementation is expected to produce a candidate diff. If the candidate is complete but Codex self-validation commands are unavailable or blocked, the task-run output should still be `completed`; Tutti checks are the authoritative validation gate.
|
|
11
11
|
Initial `task-run.md`, active `task-continuation.md`, and active pipeline `task-retry.md` receive the compact task contract fields `title / goal / scope` plus optional receipt-derived `recent_references`; each reference includes only path, final name and available/unavailable summary. Continuation adds the required `continuation` block, and pipeline self-correction retry adds the required `correction` block with the previous failure summary, machine-generated reason code, correction guidance, a required redacted / bounded diagnostic, and `promotion_failure` as either a complete object or explicit `null`. Provider transient retry and self-correction both preserve the same reference context. Provider-facing prompts do not receive task ids, task summaries, Run lineage, workflow anchors, blob ids, message ids, changed paths, docs status, checks status, or the safety audit context entry.
|
|
12
12
|
All three Run templates use a slim mutually exclusive structured output schema under `result`: exactly one of `completed.{summary,user_note_candidate}`, `completed_no_repo_changes.{summary,user_note_candidate}`, `needs_human.{title,summary,request}`, or `failed.summary`. The `summary` is a concise, human-readable result note for project members who may not inspect the code. Completed branches also require `user_note_candidate`, which is either a short user-facing final-card sentence or `null`; it must not repeat `summary` or include file paths. Checks, docs, promotion, changed paths, and other Tutti status stay in structured fields derived by the Run pipeline. They do not ask Codex to self-report docs status or changed paths; both come from Run pipeline Git diff and derived projection.
|
|
13
13
|
Run templates describe `docs/reference/` as a user-visible file exchange area. Human uploads remain under `docs/reference/files/` and `docs/reference/images/`; `docs/reference/tutti/**` is for Tutti-generated files intended for direct member review in References, not temporary output, internal notes, logs, or canonical project documentation. Generated user-visible files must be written under `docs/reference/tutti/`, with `docs/reference/tutti/<meaningful-folder>/...` used when the folder name should be shown as a References UI group. Generated files under `docs/reference/tutti/` are discovered from promoted changed paths, rendered by References automatically, and may be listed in completed task result cards.
|
|
14
14
|
Initial `task-run.md`, active `task-continuation.md`, and active pipeline `task-retry.md` also instruct Codex to write or update the single active `tutti.artifact.json` manifest only when the task creates or updates a member-viewable visual artifact. Each template includes the complete strict static and server JSON shapes: every behavior field is required, `network` is always an explicit boolean, and server `script / ready_path / port` fields are forbidden. A server package instead declares the fixed `artifact:preview` npm script in its own `package.json` and listens on the injected `HOST / PORT`. The manifest remains repo truth and is not reported through the structured output schema.
|
|
15
|
-
Active task-bound follow-up still only starts continuation Runs from `Run.needs_human`. Pipeline self-correction retry happens before a terminal Run result is recorded and
|
|
15
|
+
Active task-bound follow-up still only starts continuation Runs from `Run.needs_human`. `follow_up_check` resumes when the identified human decision is resolved in the same contract, asks for more input only while that same decision remains unresolved, and requires retasking when honoring the answer would change goal or scope; it does not delegate Run-owned implementation choices. Continuation applies the same Run boundary after considering the answers and never repeats a resolved question. Pipeline self-correction retry happens before a terminal Run result is recorded; it treats correction as machine feedback, repairs it inside the frozen contract by default, and only returns `needs_human` when the feedback exposes a genuinely unresolved human decision under the normal Run boundary.
|
|
16
16
|
All three templates state that cwd is the persistent project workspace, may contain unaccepted source and project-local runtime/dependency state from earlier failed attempts, and must not be reset, checked out, stashed, cleaned, or otherwise discarded. Continuation, provider transient retry, and self-correction reuse that same directory and accumulated state.
|
|
17
17
|
All three templates require a final Git status inspection and make the Agent responsible for maintaining the repository's own `.gitignore` according to project conventions. They do not prescribe runtime path names or force intentional generated source out of Git.
|
|
18
18
|
|
|
@@ -35,7 +35,7 @@ Use `continuation` to understand:
|
|
|
35
35
|
- what humans answered;
|
|
36
36
|
- why `follow_up_check` allowed this task to resume.
|
|
37
37
|
|
|
38
|
-
Human clarification answers may clarify or confirm work inside the frozen task contract. They must not expand `goal` or `scope` into a new task. If `continuation` is missing or
|
|
38
|
+
Human clarification answers may clarify or confirm work inside the frozen task contract. They must not expand `goal` or `scope` into a new task. If `continuation` is missing or structurally incomplete, return `failed` rather than inventing context.
|
|
39
39
|
|
|
40
40
|
# Execution Boundary
|
|
41
41
|
|
|
@@ -49,7 +49,7 @@ Human clarification answers may clarify or confirm work inside the frozen task c
|
|
|
49
49
|
- Before returning, inspect the final Git status. Maintain the repository's own `.gitignore` for project-local state that should not be versioned, following existing project conventions; keep files Git-visible when they are intentional versioned source. Do not assume fixed runtime path names.
|
|
50
50
|
- Prefer small, reviewable changes that preserve existing style and tests.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
Apply the same human-decision boundary after considering the submitted clarification. Do not repeat a resolved question. Return `needs_human` only when the answer remains materially insufficient for the same frozen contract or execution reveals a distinct unresolved human decision. Use implementation judgment for choices that remain inside the contract; return `failed` when no usable candidate can be produced.
|
|
53
53
|
|
|
54
54
|
# Context Access
|
|
55
55
|
|
|
@@ -57,7 +57,7 @@ Correct the existing candidate in place, using `correction.summary`, `correction
|
|
|
57
57
|
- Before returning, inspect the final Git status. Maintain the repository's own `.gitignore` for project-local state that should not be versioned, following existing project conventions; keep files Git-visible when they are intentional versioned source. Do not assume fixed runtime path names.
|
|
58
58
|
- Prefer small, reviewable changes that preserve existing style and tests.
|
|
59
59
|
|
|
60
|
-
|
|
60
|
+
Treat correction feedback as a machine-reported implementation problem, not as new task context. Correct it without human input when possible within the frozen contract. If it exposes an unresolved human decision under the normal Run boundary, return `needs_human` with the minimum concrete, non-secret question; do not treat correction feedback as permission to change the task's intent or constraints. Return `failed` when no usable candidate can be produced and no human decision can unblock it.
|
|
61
61
|
|
|
62
62
|
# Context Access
|
|
63
63
|
|