orizu 0.5.27 → 0.5.28

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 CHANGED
@@ -427,6 +427,25 @@ Task and dataset export support:
427
427
  - `json`
428
428
  - `jsonl`
429
429
 
430
+ Dataset `upload` and `push` create an immutable version after the rows are
431
+ acknowledged. With `--json`, upload prints one result document. A failed snapshot
432
+ reports the uploaded dataset identity so recovery can target the snapshot step.
433
+ If a write's response is lost or times out, inspect the dataset before retrying:
434
+ the last chunk may already have committed.
435
+
436
+ Dataset downloads retry transient read failures up to three attempts. Each
437
+ transfer request, including its response body, has a two-minute deadline;
438
+ `ORIZU_DATASET_TRANSFER_TIMEOUT_MS` accepts 1–300000 milliseconds. Downloads
439
+ receive the complete response before writing to the destination. Existing files
440
+ keep their inode, ownership, permissions and ACLs; writes follow existing
441
+ symlinks and update hardlinked readers. New files request mode `0600`, subject
442
+ to the filesystem's access rules. A failed network transfer leaves an existing output
443
+ untouched. The final write retains ordinary filesystem behavior: it is not
444
+ atomic, and a disk failure during that write can leave partial output.
445
+ SIGINT/SIGTERM cancels a download. Upload writes
446
+ have no additional transient retries; the existing session refresh behavior is
447
+ unchanged.
448
+
430
449
  ## Interactive And Automated Usage
431
450
 
432
451
  Many commands can prompt for missing team, project, app, dataset, or task selections in an interactive terminal. In scripts and CI, pass explicit flags instead:
@@ -0,0 +1,2 @@
1
+ export declare function transferDatasetDownload(datasetId: string, format: string, filename: string): Promise<void>;
2
+ //# sourceMappingURL=dataset-transfer-download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-transfer-download.d.ts","sourceRoot":"","sources":["../src/dataset-transfer-download.ts"],"names":[],"mappings":"AAmBA,wBAAsB,uBAAuB,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyChH"}
@@ -0,0 +1,66 @@
1
+ import { Effect, Either, Schedule } from 'effect';
2
+ import { writeFileSync } from 'node:fs';
3
+ import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
4
+ import { authedFetch } from './http.js';
5
+ class DownloadHttpError extends Error {
6
+ status;
7
+ _tag = 'DownloadHttpError';
8
+ constructor(status, detail) {
9
+ super(`Download failed (HTTP ${status}): ${detail}`);
10
+ this.status = status;
11
+ }
12
+ }
13
+ class DownloadTransportError extends Error {
14
+ _tag = 'DownloadTransportError';
15
+ constructor(cause) {
16
+ super('Dataset download transport failed', { cause });
17
+ }
18
+ }
19
+ export async function transferDatasetDownload(datasetId, format, filename) {
20
+ const timeoutMs = datasetTransferTimeoutMs();
21
+ const download = Effect.tryPromise({
22
+ try: async (signal) => {
23
+ const response = await authedFetch(`/api/cli/datasets/${encodeURIComponent(datasetId)}/download?format=${encodeURIComponent(format)}`, { signal });
24
+ if (!response.ok)
25
+ throw new DownloadHttpError(response.status, await response.text());
26
+ return new Uint8Array(await response.arrayBuffer());
27
+ },
28
+ catch: cause => {
29
+ if (cause instanceof TypeError)
30
+ return new DownloadTransportError(cause);
31
+ return cause instanceof Error ? cause : new Error('Dataset download request failed', { cause });
32
+ },
33
+ }).pipe(Effect.timeoutFail({
34
+ duration: timeoutMs,
35
+ onTimeout: () => new Error('DATASET_TRANSFER_TIMEOUT: Dataset download exceeded its deadline'),
36
+ }), Effect.retry({
37
+ times: 2,
38
+ schedule: Schedule.spaced(100),
39
+ while: error => error instanceof DownloadTransportError || (error instanceof DownloadHttpError && [429, 502, 503, 504].includes(error.status)),
40
+ }));
41
+ const fileError = (cause) => new Error(`DATASET_DOWNLOAD_FILE: Could not save dataset to ${filename}: ${cause instanceof Error ? cause.message : String(cause)}`, { cause });
42
+ const write = (bytes) => Effect.try({
43
+ // Wait for the complete response, then retain the CLI's existing in-place
44
+ // write semantics, including inode metadata, hardlinks and symlink targets.
45
+ try: () => writeFileSync(filename, bytes, { mode: 0o600 }),
46
+ catch: fileError,
47
+ });
48
+ const controller = new AbortController();
49
+ const handleInterrupt = () => controller.abort();
50
+ process.once('SIGINT', handleInterrupt);
51
+ process.once('SIGTERM', handleInterrupt);
52
+ try {
53
+ const result = await Effect.runPromise(Effect.either(Effect.flatMap(download, write)), { signal: controller.signal });
54
+ if (Either.isLeft(result))
55
+ throw result.left;
56
+ }
57
+ catch (error) {
58
+ if (controller.signal.aborted)
59
+ throw new Error('DATASET_TRANSFER_INTERRUPTED: Dataset download cancelled');
60
+ throw error;
61
+ }
62
+ finally {
63
+ process.removeListener('SIGINT', handleInterrupt);
64
+ process.removeListener('SIGTERM', handleInterrupt);
65
+ }
66
+ }
@@ -0,0 +1,2 @@
1
+ export declare function datasetTransferTimeoutMs(): number;
2
+ //# sourceMappingURL=dataset-transfer-policy.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-transfer-policy.d.ts","sourceRoot":"","sources":["../src/dataset-transfer-policy.ts"],"names":[],"mappings":"AAAA,wBAAgB,wBAAwB,IAAI,MAAM,CAOjD"}
@@ -0,0 +1,8 @@
1
+ export function datasetTransferTimeoutMs() {
2
+ const configured = process.env.ORIZU_DATASET_TRANSFER_TIMEOUT_MS;
3
+ const timeoutMs = configured === undefined ? 120_000 : Number(configured);
4
+ if (!Number.isSafeInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 300_000) {
5
+ throw new Error('DATASET_TRANSFER_CONFIG: ORIZU_DATASET_TRANSFER_TIMEOUT_MS must be an integer from 1 to 300000');
6
+ }
7
+ return timeoutMs;
8
+ }
@@ -0,0 +1,42 @@
1
+ export interface DatasetUploadResponse {
2
+ dataset: {
3
+ id: string;
4
+ name: string;
5
+ rowCount: number;
6
+ sourceType: string;
7
+ url?: string;
8
+ };
9
+ readmeVersion?: {
10
+ id: string;
11
+ version_num: number;
12
+ created_at: string;
13
+ } | null;
14
+ }
15
+ interface DatasetSnapshot {
16
+ datasetVersion: {
17
+ id: string;
18
+ };
19
+ }
20
+ type UploadRows = Array<Record<string, unknown>>;
21
+ type UploadSource = {
22
+ kind: 'jsonl';
23
+ file: string;
24
+ } | {
25
+ kind: 'rows';
26
+ rows: UploadRows;
27
+ sourceType: 'csv' | 'json' | 'jsonl';
28
+ };
29
+ export interface DatasetUploadOptions {
30
+ project: string;
31
+ name: string;
32
+ source: UploadSource;
33
+ readmeMarkdown: string | null;
34
+ json: boolean;
35
+ onChunk: (index: number, rowCount: number) => void;
36
+ }
37
+ export declare function transferDatasetUpload(options: DatasetUploadOptions): Promise<{
38
+ data: DatasetUploadResponse;
39
+ version: DatasetSnapshot;
40
+ }>;
41
+ export {};
42
+ //# sourceMappingURL=dataset-transfer-upload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dataset-transfer-upload.d.ts","sourceRoot":"","sources":["../src/dataset-transfer-upload.ts"],"names":[],"mappings":"AASA,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IACzF,aAAa,CAAC,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,WAAW,EAAE,MAAM,CAAC;QAAC,UAAU,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;CAC/E;AAED,UAAU,eAAe;IAAG,cAAc,EAAE;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE;AAE5D,KAAK,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAChD,KAAK,YAAY,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,UAAU,CAAC;IAAC,UAAU,EAAE,KAAK,GAAG,MAAM,GAAG,OAAO,CAAA;CAAE,CAAA;AAE9H,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,YAAY,CAAA;IACpB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;CACnD;AAoHD,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,qBAAqB,CAAC;IAAC,OAAO,EAAE,eAAe,CAAA;CAAE,CAAC,CAEvI"}
@@ -0,0 +1,110 @@
1
+ import { Context, Effect, Either, Layer } from 'effect';
2
+ import { ensureDatasetUploadSnapshot } from './dataset-upload-snapshot.js';
3
+ import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
4
+ import { extractErrorMessage } from './error-response.js';
5
+ import { authedFetch } from './http.js';
6
+ import { parseJsonResponse, sanitizeTerminalText } from './json-response.js';
7
+ import { streamJsonlRowChunks } from './jsonl-stream.js';
8
+ class DatasetWriteFailure extends Error {
9
+ outcome;
10
+ _tag = 'DatasetWriteFailure';
11
+ constructor(message, outcome, options) {
12
+ super(message, options);
13
+ this.outcome = outcome;
14
+ }
15
+ }
16
+ class DatasetInputFailure extends Error {
17
+ _tag = 'DatasetInputFailure';
18
+ constructor(cause) { super(cause instanceof Error ? cause.message : String(cause), { cause }); }
19
+ }
20
+ const DatasetUploadApi = Context.GenericTag('orizu/DatasetUploadApi');
21
+ // Only the existing authentication client's rejected-token refresh may replay a
22
+ // request. No transient/transport retries belong around these non-idempotent writes.
23
+ function post(path, body, context, prefix, timeoutMs) {
24
+ return Effect.suspend(() => {
25
+ let requestStarted = false;
26
+ return Effect.tryPromise({
27
+ try: async (signal) => {
28
+ const response = await authedFetch(path, {
29
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
30
+ body: JSON.stringify(body), signal,
31
+ }, () => { requestStarted = true; });
32
+ if (!response.ok) {
33
+ throw new DatasetWriteFailure(`${prefix}: ${await extractErrorMessage(response)}`, response.status >= 500 ? 'unknown' : 'rejected');
34
+ }
35
+ return await parseJsonResponse(response, context);
36
+ },
37
+ catch: cause => cause instanceof DatasetWriteFailure ? cause : new DatasetWriteFailure(cause instanceof Error ? cause.message : String(cause), requestStarted ? 'unknown' : 'rejected', { cause }),
38
+ }).pipe(Effect.timeoutFail({
39
+ duration: timeoutMs,
40
+ onTimeout: () => new DatasetWriteFailure(`DATASET_TRANSFER_TIMEOUT: ${context} exceeded its deadline`, requestStarted ? 'unknown' : 'rejected'),
41
+ }));
42
+ });
43
+ }
44
+ function liveUploadApi(options) {
45
+ const timeoutMs = datasetTransferTimeoutMs();
46
+ return Layer.succeed(DatasetUploadApi, {
47
+ create: (rows, sourceType) => post('/api/cli/datasets/upload', {
48
+ projectSlug: options.project, name: options.name, rows, sourceType,
49
+ ...(options.readmeMarkdown !== null ? { readmeMarkdown: options.readmeMarkdown } : {}),
50
+ }, 'Dataset upload', 'Upload failed', timeoutMs),
51
+ append: (id, rows) => post(`/api/cli/datasets/${encodeURIComponent(id)}/rows`, { rows }, 'Dataset append', 'Append failed', timeoutMs),
52
+ snapshot: id => post(`/api/cli/datasets/${encodeURIComponent(id)}/versions`, {}, 'Dataset version create', 'Failed to create dataset version', timeoutMs),
53
+ });
54
+ }
55
+ async function expectedPromise(program) {
56
+ const result = await Effect.runPromise(Effect.either(program));
57
+ if (Either.isLeft(result))
58
+ throw result.left;
59
+ return result.right;
60
+ }
61
+ function uploadProgram(options) {
62
+ return Effect.gen(function* () {
63
+ const api = yield* DatasetUploadApi;
64
+ const chunks = yield* Effect.acquireRelease(Effect.sync(() => options.source.kind === 'jsonl'
65
+ ? streamJsonlRowChunks(options.source.file)
66
+ : (async function* () { if (options.source.kind === 'rows')
67
+ yield options.source.rows; })()), iterator => Effect.promise(async () => { await iterator.return(undefined); }));
68
+ let data;
69
+ let confirmedRows = 0;
70
+ let chunkIndex = 0;
71
+ while (true) {
72
+ const next = yield* Effect.tryPromise({ try: () => chunks.next(), catch: cause => new DatasetInputFailure(cause) }).pipe(Effect.mapError(error => new Error(data
73
+ ? `Upload stopped while reading the next JSONL chunk: ${error.message}\nDataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) was created and ${confirmedRows} rows were uploaded. Fix the file, remove the first ${confirmedRows} rows, and run orizu datasets append --dataset ${data.dataset.id} --file <remaining-file>.`
74
+ : error.message)));
75
+ if (next.done)
76
+ break;
77
+ chunkIndex++;
78
+ if (options.source.kind === 'jsonl')
79
+ yield* Effect.sync(() => options.onChunk(chunkIndex, next.value.length));
80
+ const write = data
81
+ ? api.append(data.dataset.id, next.value).pipe(Effect.map(result => ({
82
+ data: { ...data, dataset: { ...data.dataset, rowCount: result.dataset.rowCount } },
83
+ added: result.appendedCount,
84
+ })))
85
+ : api.create(next.value, options.source.kind === 'jsonl' ? 'jsonl' : options.source.sourceType).pipe(Effect.map(result => ({ data: result, added: result.dataset.rowCount })));
86
+ const result = yield* write.pipe(Effect.mapError(error => {
87
+ if (error.outcome === 'unknown') {
88
+ return new Error(`DATASET_UPLOAD_UNCERTAIN: Chunk ${chunkIndex} may have committed, but its outcome could not be confirmed: ${error.message}\n` +
89
+ (data ? `Dataset ${sanitizeTerminalText(data.dataset.id)} has ${confirmedRows} confirmed uploaded rows. ` : 'Dataset creation could not be confirmed. ') +
90
+ 'Inspect the dataset and reconcile row IDs before retrying; do not replay this chunk blindly.');
91
+ }
92
+ return new Error(data
93
+ ? `Chunk ${chunkIndex} failed: ${error.message}\nDataset ${sanitizeTerminalText(data.dataset.name)} (${sanitizeTerminalText(data.dataset.id)}) was created and ${confirmedRows} rows were uploaded. To retry, remove the first ${confirmedRows} rows from your file and run orizu datasets append --dataset ${data.dataset.id} --file <remaining-file>.`
94
+ : error.message);
95
+ }));
96
+ data = result.data;
97
+ confirmedRows += result.added;
98
+ }
99
+ if (!data)
100
+ return yield* Effect.fail(new Error('Dataset file contains no rows'));
101
+ const version = yield* Effect.tryPromise({
102
+ try: () => ensureDatasetUploadSnapshot(data.dataset, options.json, id => expectedPromise(api.snapshot(id))),
103
+ catch: cause => cause instanceof Error ? cause : new Error(String(cause)),
104
+ });
105
+ return { data, version };
106
+ });
107
+ }
108
+ export function transferDatasetUpload(options) {
109
+ return expectedPromise(uploadProgram(options).pipe(Effect.scoped, Effect.provide(liveUploadApi(options))));
110
+ }
@@ -156,20 +156,19 @@ export interface OpenCodeConfigOptions {
156
156
  permission?: Record<string, unknown>;
157
157
  }
158
158
  /**
159
- * Tools DENIED in a hosted headless run (ALI-1037). OpenCode exposes a `question`
160
- * permission key (docs/permissions) that gates the interactive "ask the user a
161
- * question" tool. In an unattended sandbox there is no human to answer it, and
162
- * OpenCode's only external reply endpoint — `POST /session/{id}/permissions/{permID}`
163
- * — takes a BOOLEAN allow/deny, so it cannot select one of the tool's multiple
164
- * options anyway. Denying the tool makes the model's ask fail fast (surfaced back
165
- * to it as a tool error) so it proceeds autonomously per the standing preamble
166
- * (HOSTED_TASK_PREAMBLE), instead of blocking the run forever. This is the
167
- * documented fallback path in ALI-1037 (answer-externally is not cleanly possible).
159
+ * Tools denied for CLI-origin headless runs (ALI-1037). OpenCode 1.14.41 removes
160
+ * a tool from the model toolset when its permission rule is `deny` on `*`; it does
161
+ * not expose the tool and report a denial result. Hosted-web therefore overrides
162
+ * `question` to `allow`, intercepts its measured running part, and quiesces the
163
+ * turn while waiting for the human. CLI-origin runs retain this deny list because
164
+ * no human reply flow exists there.
168
165
  */
169
166
  export declare const HEADLESS_DENIED_TOOLS: readonly ["question"];
170
167
  /** The default headless permission map: allow everything, deny the interactive
171
168
  * question tool (see HEADLESS_DENIED_TOOLS). */
172
169
  export declare function defaultHeadlessPermission(): Record<string, unknown>;
170
+ /** Select the OpenCode permission map for the session's human-input surface. */
171
+ export declare function permissionForOrigin(origin?: 'hosted-web' | 'cli'): Record<string, unknown>;
173
172
  /**
174
173
  * Build the `OPENCODE_CONFIG_CONTENT` payload OpenCode reads at boot (mirrors
175
174
  * OpenInspect's `start_opencode`). Kept pure so the launch command and the
@@ -1 +1 @@
1
- {"version":3,"file":"hosted-harness-opencode.d.ts","sourceRoot":"","sources":["../src/hosted-harness-opencode.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hosted-harness-opencode.d.ts","sourceRoot":"","sources":["../src/hosted-harness-opencode.ts"],"names":[],"mappings":"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"}
@@ -931,15 +931,12 @@ export async function awaitOpenCodeModelResolvable(opts) {
931
931
  };
932
932
  }
933
933
  /**
934
- * Tools DENIED in a hosted headless run (ALI-1037). OpenCode exposes a `question`
935
- * permission key (docs/permissions) that gates the interactive "ask the user a
936
- * question" tool. In an unattended sandbox there is no human to answer it, and
937
- * OpenCode's only external reply endpoint — `POST /session/{id}/permissions/{permID}`
938
- * — takes a BOOLEAN allow/deny, so it cannot select one of the tool's multiple
939
- * options anyway. Denying the tool makes the model's ask fail fast (surfaced back
940
- * to it as a tool error) so it proceeds autonomously per the standing preamble
941
- * (HOSTED_TASK_PREAMBLE), instead of blocking the run forever. This is the
942
- * documented fallback path in ALI-1037 (answer-externally is not cleanly possible).
934
+ * Tools denied for CLI-origin headless runs (ALI-1037). OpenCode 1.14.41 removes
935
+ * a tool from the model toolset when its permission rule is `deny` on `*`; it does
936
+ * not expose the tool and report a denial result. Hosted-web therefore overrides
937
+ * `question` to `allow`, intercepts its measured running part, and quiesces the
938
+ * turn while waiting for the human. CLI-origin runs retain this deny list because
939
+ * no human reply flow exists there.
943
940
  */
944
941
  export const HEADLESS_DENIED_TOOLS = ['question'];
945
942
  /** The default headless permission map: allow everything, deny the interactive
@@ -950,6 +947,12 @@ export function defaultHeadlessPermission() {
950
947
  permission[tool] = 'deny';
951
948
  return permission;
952
949
  }
950
+ /** Select the OpenCode permission map for the session's human-input surface. */
951
+ export function permissionForOrigin(origin) {
952
+ if (origin === 'hosted-web')
953
+ return { ...defaultHeadlessPermission(), question: 'allow' };
954
+ return defaultHeadlessPermission();
955
+ }
953
956
  /**
954
957
  * Build the `OPENCODE_CONFIG_CONTENT` payload OpenCode reads at boot (mirrors
955
958
  * OpenInspect's `start_opencode`). Kept pure so the launch command and the
@@ -1 +1 @@
1
- {"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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"}
@@ -42,7 +42,7 @@ import { isHarnessTerminalKind } from './hosted-harness.js';
42
42
  import { SETUP_HOOK_RELATIVE_PATH, composeHostedTaskPrompt, parsePrebakedMarker, resolveHostedTaskPreamble, resolvePrebakedMarkerPath, } from './hosted-runtime-assets.js';
43
43
  import { harvestWorkspace } from './hosted-harvest.js';
44
44
  import { annotateHeadlessQuestions, withIdleWatchdog } from './hosted-headless.js';
45
- import { DEFAULT_PROMPT_MAX_DURATION_MS, OPENCODE_PINNED_VERSION, awaitOpenCodeModelResolvable, createOpenCodeHarness, spawnOpenCode, } from './hosted-harness-opencode.js';
45
+ import { DEFAULT_PROMPT_MAX_DURATION_MS, OPENCODE_PINNED_VERSION, awaitOpenCodeModelResolvable, createOpenCodeHarness, permissionForOrigin, spawnOpenCode, } from './hosted-harness-opencode.js';
46
46
  import { createClaudeAgentHarness } from './hosted-harness-claude.js';
47
47
  import { INJECTED_ENV_VARS_ENV } from './hosted-environment.js';
48
48
  import { interceptHostedQuestions } from './hosted-question.js';
@@ -444,6 +444,7 @@ export async function runHostedLoop(opts) {
444
444
  // connection-refused "fetch failed" (ALI-1034).
445
445
  spawned = await spawnImpl({
446
446
  model: context.model,
447
+ permission: permissionForOrigin(context.sessionOrigin),
447
448
  cwd: context.workspaceDir,
448
449
  port: context.opencodePort,
449
450
  env: buildOpenCodeSpawnEnv(context),
@@ -522,8 +523,9 @@ export async function runHostedLoop(opts) {
522
523
  content: composeHostedTaskPrompt(taskPrompt, opts.taskPreamble ?? resolveHostedTaskPreamble(context.sessionOrigin)),
523
524
  author: context.author,
524
525
  };
525
- // Drive the prompt through (a) question auto-handling annotation and (b) the
526
- // idle watchdog (ALI-1037): no harness event for `idleTimeoutMs` aborts the
526
+ // Drive the prompt through question-deny annotation unless hosted-web
527
+ // OpenCode interception owns the outcome, then through the idle watchdog
528
+ // (ALI-1037): no harness event for `idleTimeoutMs` aborts the
527
529
  // prompt and throws `AgentStalledError` → the catch below harvests, then
528
530
  // finishes the run `failed`. The prompt runs under a CHILD AbortController
529
531
  // linked to the loop signal so the watchdog can abort the in-flight prompt
@@ -570,9 +572,10 @@ export async function runHostedLoop(opts) {
570
572
  interruptSignal?.addEventListener('abort', handleInterrupt, { once: true });
571
573
  const terminalSummary = { agentSessionId };
572
574
  let hasQuestionOutcome = false;
573
- const annotated = annotateHeadlessQuestions(harness.runPrompt(prompt, promptController.signal));
574
- const questionAware = harnessKind === 'opencode' && context.sessionOrigin === 'hosted-web'
575
- ? interceptHostedQuestions(annotated, {
575
+ const promptEvents = harness.runPrompt(prompt, promptController.signal);
576
+ const hasHostedQuestionInterception = harnessKind === 'opencode' && context.sessionOrigin === 'hosted-web';
577
+ const intercepted = hasHostedQuestionInterception
578
+ ? interceptHostedQuestions(promptEvents, {
576
579
  onDetected: question => {
577
580
  hasQuestionOutcome = true;
578
581
  terminalSummary.outcome = 'question_pending';
@@ -588,7 +591,10 @@ export async function runHostedLoop(opts) {
588
591
  onPersisted: beginQuiescence,
589
592
  onInvalidPersisted: beginQuiescence,
590
593
  })
591
- : annotated;
594
+ : promptEvents;
595
+ const questionAware = hasHostedQuestionInterception
596
+ ? intercepted
597
+ : annotateHeadlessQuestions(intercepted);
592
598
  const interruptAware = (async function* () {
593
599
  for await (const event of questionAware) {
594
600
  if (isHarnessTerminalKind(event.kind)) {
@@ -639,8 +645,8 @@ export async function runHostedLoop(opts) {
639
645
  promptController.abort();
640
646
  await harness.stop();
641
647
  },
642
- // The `question` tool is DENIED headless, so a model stuck retrying it
643
- // emits a stream of synthetic `question_auto_answered` events. Those are
648
+ // Without hosted-web OpenCode interception, a model stuck retrying a
649
+ // denied question emits synthetic `question_auto_answered` events. Those are
644
650
  // NOT progress — excluding them lets a deny/retry loop trip agent_stalled
645
651
  // instead of resetting the idle timer forever (ALI-1069).
646
652
  isProgress: event => event.kind !== 'question_auto_answered',
package/dist/http.d.ts CHANGED
@@ -25,5 +25,5 @@ export interface LogoutResult {
25
25
  export declare function captureAuthenticatedRequestContext(options?: {
26
26
  allowRefresh?: boolean;
27
27
  }): AuthenticatedRequestContext;
28
- export declare function authedFetch(path: string, init?: RequestInit): Promise<Response>;
28
+ export declare function authedFetch(path: string, init?: RequestInit, onRequestStart?: () => void): Promise<Response>;
29
29
  //# sourceMappingURL=http.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AASA,OAAO,EAAkB,WAAW,EAAoB,MAAM,mBAAmB,CAAA;AASjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,QAEhD;AAED,wBAAgB,cAAc,CAAC,KAAK,GAAE,WAA0B,GAAG,MAAM,CAiBxE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,WAA0B,GAAG,MAAM,CAY7E;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAWD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,QAmBzD;AAqFD,wBAAgB,+BAA+B,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAE/E;AAkGD,MAAM,MAAM,0BAA0B,GAAG,aAAa,GAAG,kBAAkB,GAAG,QAAQ,CAAA;AAEtF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAA;IAC3C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC1D,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAC3D;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,sBAAsB,EAAE,OAAO,CAAA;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,2BAA2B,CAgI7B;AAED,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,qBAiErE"}
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AASA,OAAO,EAAkB,WAAW,EAAoB,MAAM,mBAAmB,CAAA;AASjF,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,QAEhD;AAED,wBAAgB,cAAc,CAAC,KAAK,GAAE,WAA0B,GAAG,MAAM,CAiBxE;AAED,wBAAgB,mBAAmB,CAAC,KAAK,GAAE,WAA0B,GAAG,MAAM,CAY7E;AAED,wBAAgB,UAAU,IAAI,MAAM,CAEnC;AAWD,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,MAAM,QAmBzD;AAqFD,wBAAgB,+BAA+B,IAAI,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,CAE/E;AAkGD,MAAM,MAAM,0BAA0B,GAAG,aAAa,GAAG,kBAAkB,GAAG,QAAQ,CAAA;AAEtF,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,MAAM,EAAE,0BAA0B,CAAA;IAC3C,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAA;IAC1D,MAAM,CAAC,MAAM,CAAC,EAAE,WAAW,GAAG,IAAI,GAAG,OAAO,CAAC,YAAY,CAAC,CAAA;CAC3D;AAED,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,sBAAsB,EAAE,OAAO,CAAA;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,kCAAkC,CAChD,OAAO,GAAE;IAAE,YAAY,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,2BAA2B,CAgI7B;AAID,wBAAsB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,WAAgB,EAAE,cAAc,CAAC,EAAE,MAAM,IAAI,qBAqElG"}
package/dist/http.js CHANGED
@@ -311,7 +311,13 @@ export function captureAuthenticatedRequestContext(options = {}) {
311
311
  },
312
312
  };
313
313
  }
314
- export async function authedFetch(path, init = {}) {
314
+ // The optional observer runs immediately before a resource fetch, after auth
315
+ // preflight. Authentication refresh requests do not mark the resource as sent.
316
+ export async function authedFetch(path, init = {}, onRequestStart) {
317
+ const fetchResource = (url, requestInit) => {
318
+ onRequestStart?.();
319
+ return fetch(url, requestInit);
320
+ };
315
321
  const baseUrl = resolveBaseUrl();
316
322
  assertSecureTokenTransport(baseUrl);
317
323
  // In-sandbox pre-auth (ALI-1044): a bearer supplied via ORIZU_TOKEN /
@@ -321,7 +327,7 @@ export async function authedFetch(path, init = {}) {
321
327
  // the file and the next request naturally picks up the new bearer.
322
328
  const envBearer = resolveEnvBearerToken();
323
329
  if (envBearer) {
324
- return await fetch(`${baseUrl}${path}`, {
330
+ return await fetchResource(`${baseUrl}${path}`, {
325
331
  ...init,
326
332
  headers: {
327
333
  ...(init.headers || {}),
@@ -336,7 +342,7 @@ export async function authedFetch(path, init = {}) {
336
342
  }
337
343
  const resolved = await refreshExpiredStoredCredentials(baseUrl, credentials, init.signal);
338
344
  let activeCredentials = resolved.credentials;
339
- let response = await fetch(`${baseUrl}${path}`, {
345
+ let response = await fetchResource(`${baseUrl}${path}`, {
340
346
  ...init,
341
347
  headers: {
342
348
  ...(init.headers || {}),
@@ -352,7 +358,7 @@ export async function authedFetch(path, init = {}) {
352
358
  throw new Error('ORIZU_AUTH_CONTEXT_CHANGED: stored credentials changed after this request began; the request was not replayed.');
353
359
  }
354
360
  activeCredentials = requireCurrentCredentials(baseUrl, refreshResult.credentials);
355
- response = await fetch(`${baseUrl}${path}`, {
361
+ response = await fetchResource(`${baseUrl}${path}`, {
356
362
  ...init,
357
363
  headers: {
358
364
  ...(init.headers || {}),
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAyHgF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAqChJ,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAwV5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAUD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAyBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAsBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,GAAG,CAqB5E;AA6BD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBtD;AAqiKD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,eAAO,MAAM,oBAAoB,aAgB/B,CAAA;AAEF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAaxH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBA6gBzD"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAwHgF,OAAO,EAAE,kBAAkB,EAAE,MAAM,6BAA6B,CAAA;AAqChJ,eAAO,MAAM,wBAAwB,GAAI,iBAAiB,MAAM;;;EACJ,CAAA;AAE5D,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAA;AAuU5E,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAEvD;AAUD,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAOnD;AAyBD,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE5D;AAsBD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,CAAC,EAAE,MAAM,GAAG,GAAG,CAqB5E;AA6BD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBtD;AA28JD,wBAAgB,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,CA0B1G;AAED,eAAO,MAAM,iBAAiB,SAAU,CAAA;AACxC,eAAO,MAAM,uBAAuB,QAAkB,CAAA;AACtD,eAAO,MAAM,oBAAoB,aAgB/B,CAAA;AAEF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,iBAAiB,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,UAAU,CAaxH;AAmMD,wBAAsB,IAAI,CAAC,OAAO,WAAwB,iBA6gBzD"}