orizu 0.5.26 → 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.
Files changed (164) hide show
  1. package/README.md +19 -0
  2. package/dist/artifacts-git-runtime.d.ts +34 -0
  3. package/dist/artifacts-git-runtime.d.ts.map +1 -0
  4. package/dist/artifacts-git-runtime.js +312 -0
  5. package/dist/cloudflare-artifacts-git-remote.d.ts +5 -0
  6. package/dist/cloudflare-artifacts-git-remote.d.ts.map +1 -0
  7. package/dist/cloudflare-artifacts-git-remote.js +21 -0
  8. package/dist/dataset-transfer-download.d.ts +2 -0
  9. package/dist/dataset-transfer-download.d.ts.map +1 -0
  10. package/dist/dataset-transfer-download.js +66 -0
  11. package/dist/dataset-transfer-policy.d.ts +2 -0
  12. package/dist/dataset-transfer-policy.d.ts.map +1 -0
  13. package/dist/dataset-transfer-policy.js +8 -0
  14. package/dist/dataset-transfer-upload.d.ts +42 -0
  15. package/dist/dataset-transfer-upload.d.ts.map +1 -0
  16. package/dist/dataset-transfer-upload.js +110 -0
  17. package/dist/git-credential.d.ts.map +1 -1
  18. package/dist/git-credential.js +1 -1
  19. package/dist/git-recovery-environment.d.ts +1 -0
  20. package/dist/git-recovery-environment.d.ts.map +1 -1
  21. package/dist/git-recovery-environment.js +1 -1
  22. package/dist/help.d.ts.map +1 -1
  23. package/dist/help.js +44 -11
  24. package/dist/hosted-boot.d.ts +75 -2
  25. package/dist/hosted-boot.d.ts.map +1 -1
  26. package/dist/hosted-boot.js +585 -102
  27. package/dist/hosted-bootstrap.d.ts +2 -0
  28. package/dist/hosted-bootstrap.d.ts.map +1 -1
  29. package/dist/hosted-bootstrap.js +12 -3
  30. package/dist/hosted-harness-opencode.d.ts +8 -9
  31. package/dist/hosted-harness-opencode.d.ts.map +1 -1
  32. package/dist/hosted-harness-opencode.js +126 -24
  33. package/dist/hosted-harness.d.ts +3 -0
  34. package/dist/hosted-harness.d.ts.map +1 -1
  35. package/dist/hosted-harness.js +4 -1
  36. package/dist/hosted-harvest.d.ts +30 -9
  37. package/dist/hosted-harvest.d.ts.map +1 -1
  38. package/dist/hosted-harvest.js +222 -28
  39. package/dist/hosted-loop-lifecycle.d.ts +15 -3
  40. package/dist/hosted-loop-lifecycle.d.ts.map +1 -1
  41. package/dist/hosted-loop-lifecycle.js +6 -4
  42. package/dist/hosted-loop.d.ts +47 -7
  43. package/dist/hosted-loop.d.ts.map +1 -1
  44. package/dist/hosted-loop.js +310 -64
  45. package/dist/hosted-question-limits.d.ts +7 -0
  46. package/dist/hosted-question-limits.d.ts.map +1 -0
  47. package/dist/hosted-question-limits.js +6 -0
  48. package/dist/hosted-question.d.ts +27 -0
  49. package/dist/hosted-question.d.ts.map +1 -0
  50. package/dist/hosted-question.js +118 -0
  51. package/dist/hosted-run-event-sink.d.ts +10 -5
  52. package/dist/hosted-run-event-sink.d.ts.map +1 -1
  53. package/dist/hosted-run-event-sink.js +12 -8
  54. package/dist/hosted-runtime-assets.d.ts +16 -7
  55. package/dist/hosted-runtime-assets.d.ts.map +1 -1
  56. package/dist/hosted-runtime-assets.js +40 -12
  57. package/dist/hosted-session-cli.d.ts.map +1 -1
  58. package/dist/hosted-session-cli.js +10 -19
  59. package/dist/http.d.ts +1 -1
  60. package/dist/http.d.ts.map +1 -1
  61. package/dist/http.js +10 -4
  62. package/dist/index.d.ts.map +1 -1
  63. package/dist/index.js +113 -92
  64. package/dist/instruction-set-gepa-launch.d.ts +1 -1
  65. package/dist/instruction-set-gepa-launch.d.ts.map +1 -1
  66. package/dist/instruction-set-gepa-launch.js +5 -3
  67. package/dist/instruction-set-loader.d.ts.map +1 -1
  68. package/dist/instruction-set-loader.js +63 -2
  69. package/dist/instruction-set-lock/index.d.ts +51 -0
  70. package/dist/instruction-set-lock/index.d.ts.map +1 -0
  71. package/dist/instruction-set-lock/index.js +459 -0
  72. package/dist/instruction-set-lock/schema.v1.d.ts +136 -0
  73. package/dist/instruction-set-lock/schema.v1.d.ts.map +1 -0
  74. package/dist/instruction-set-lock/schema.v1.js +155 -0
  75. package/dist/instruction-set-provenance.d.ts +26 -0
  76. package/dist/instruction-set-provenance.d.ts.map +1 -0
  77. package/dist/instruction-set-provenance.js +68 -0
  78. package/dist/instruction-set-sync/helpers.d.ts +22 -0
  79. package/dist/instruction-set-sync/helpers.d.ts.map +1 -0
  80. package/dist/instruction-set-sync/helpers.js +677 -0
  81. package/dist/instruction-set-sync/index.d.ts +60 -0
  82. package/dist/instruction-set-sync/index.d.ts.map +1 -0
  83. package/dist/instruction-set-sync/index.js +689 -0
  84. package/dist/instruction-set-update/index.d.ts +42 -0
  85. package/dist/instruction-set-update/index.d.ts.map +1 -0
  86. package/dist/instruction-set-update/index.js +445 -0
  87. package/dist/instruction-set-verify/index.d.ts +27 -0
  88. package/dist/instruction-set-verify/index.d.ts.map +1 -0
  89. package/dist/instruction-set-verify/index.js +865 -0
  90. package/dist/instruction-sets-cli.d.ts +1 -4
  91. package/dist/instruction-sets-cli.d.ts.map +1 -1
  92. package/dist/instruction-sets-cli.js +220 -109
  93. package/dist/model-config-fields.d.ts +35 -0
  94. package/dist/model-config-fields.d.ts.map +1 -0
  95. package/dist/model-config-fields.js +28 -0
  96. package/dist/optimizations-list-cli.d.ts.map +1 -1
  97. package/dist/optimizations-list-cli.js +7 -4
  98. package/dist/score-format.d.ts +2 -0
  99. package/dist/score-format.d.ts.map +1 -0
  100. package/dist/score-format.js +18 -0
  101. package/dist/workbench-repo-contract.d.ts +65 -0
  102. package/dist/workbench-repo-contract.d.ts.map +1 -0
  103. package/dist/workbench-repo-contract.js +250 -0
  104. package/dist/workspace-bootstrap-job.d.ts +45 -0
  105. package/dist/workspace-bootstrap-job.d.ts.map +1 -0
  106. package/dist/workspace-bootstrap-job.js +431 -0
  107. package/dist/workspace-bootstrap-seed.d.ts +74 -0
  108. package/dist/workspace-bootstrap-seed.d.ts.map +1 -0
  109. package/dist/workspace-bootstrap-seed.js +209 -0
  110. package/package.json +4 -3
  111. package/src/artifacts-git-runtime.ts +360 -0
  112. package/src/cloudflare-artifacts-git-remote.ts +24 -0
  113. package/src/dataset-transfer-download.ts +61 -0
  114. package/src/dataset-transfer-policy.ts +8 -0
  115. package/src/dataset-transfer-upload.ts +145 -0
  116. package/src/git-credential.ts +1 -2
  117. package/src/git-recovery-environment.ts +1 -1
  118. package/src/help.ts +44 -11
  119. package/src/hosted-boot.ts +644 -109
  120. package/src/hosted-bootstrap.ts +14 -3
  121. package/src/hosted-harness-opencode.ts +130 -28
  122. package/src/hosted-harness.ts +4 -1
  123. package/src/hosted-harvest.ts +258 -33
  124. package/src/hosted-loop-lifecycle.ts +19 -5
  125. package/src/hosted-loop.ts +340 -68
  126. package/src/hosted-question-limits.ts +6 -0
  127. package/src/hosted-question.ts +145 -0
  128. package/src/hosted-run-event-sink.ts +24 -12
  129. package/src/hosted-runtime-assets.ts +51 -11
  130. package/src/hosted-session-cli.ts +10 -24
  131. package/src/http.ts +10 -4
  132. package/src/index.ts +108 -134
  133. package/src/instruction-set-gepa-launch.ts +4 -4
  134. package/src/instruction-set-loader.ts +53 -2
  135. package/src/instruction-set-lock/index.ts +561 -0
  136. package/src/instruction-set-lock/schema.v1.json +141 -0
  137. package/src/instruction-set-lock/schema.v1.ts +155 -0
  138. package/src/instruction-set-provenance.ts +89 -0
  139. package/src/instruction-set-sync/helpers.ts +707 -0
  140. package/src/instruction-set-sync/index.ts +791 -0
  141. package/src/instruction-set-update/index.ts +488 -0
  142. package/src/instruction-set-verify/index.ts +1067 -0
  143. package/src/instruction-sets-cli.ts +226 -114
  144. package/src/model-config-fields.ts +31 -0
  145. package/src/optimizations-list-cli.ts +7 -4
  146. package/src/score-format.ts +16 -0
  147. package/src/workbench-repo-contract.ts +314 -0
  148. package/src/workspace-bootstrap-job.ts +611 -0
  149. package/src/workspace-bootstrap-seed.ts +350 -0
  150. package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +46 -2
  151. package/vendor/skills/orizu/SKILL.md +4 -2
  152. package/vendor/skills/orizu/flows/promote.md +4 -0
  153. package/vendor/skills/orizu/references/assess-and-plan.md +1 -1
  154. package/vendor/skills/orizu/references/authority-map.md +1 -1
  155. package/vendor/skills/orizu/references/cli-reference.md +151 -35
  156. package/vendor/skills/orizu/references/instructions-after-prompts.md +3 -3
  157. package/vendor/skills/orizu/references/migrate-existing-gepa-setup.md +5 -6
  158. package/vendor/skills/orizu/references/migrate-hand-rolled-instruction-layout.md +252 -0
  159. package/vendor/skills/orizu/references/optimization-reports.md +5 -4
  160. package/vendor/skills/orizu/references/optimization-with-gepa.md +1 -1
  161. package/vendor/skills/orizu/references/orizu-in-your-codebase.md +195 -0
  162. package/vendor/skills/orizu/references/primer.md +4 -2
  163. package/vendor/skills/orizu/references/prompt-control-plane.md +33 -29
  164. package/vendor/skills/orizu/references/vocabulary.md +1 -1
@@ -11,31 +11,139 @@
11
11
  * 2. If dirty: `git add -A` then `git commit` attributed to the agent identity
12
12
  * (AGENT_GIT_IDENTITY) with message `checkpoint: run <runId> auto-harvest`.
13
13
  * 3. If there is anything unpushed (a fresh checkpoint OR pre-existing local
14
- * commits the agent made but did not push), `git push` to the session branch
15
- * (the repo-local credential helper configured by bootstrap authorizes it).
14
+ * commits the agent made but did not push), push the exact HEAD SHA to the
15
+ * session branch at bootstrap's independently validated repository locator.
16
16
  * 4. Return a typed outcome the loop records as a run event: `work_persisted`
17
- * (sha + files), `work_none` (clean, nothing to push), or `work_persist_failed`
18
- * (error). Harvest NEVER throws — a harvest failure must not change the run's
19
- * terminal status; it is recorded and the run proceeds to terminal.
17
+ * (sha + files), `work_none` (clean, nothing to push), `work_not_inspected`
18
+ * (safety-gated), or `work_persist_failed` (error). Harvest NEVER throws —
19
+ * a harvest failure must not change the run's terminal status; it is recorded
20
+ * and the run proceeds to terminal.
20
21
  *
21
22
  * The loop runs IN the sandbox, so git is driven with `child_process` (matching
22
23
  * `defaultRunSetupHook` / `installOpenCodePinned` in `hosted-loop.ts`). `exec` is
23
- * injectable so the whole thing is unit-testable with no real git.
24
+ * injectable so the whole thing is unit-testable with no real git; the HTTPS
25
+ * smart-Git boundary tests independently prove the production transport.
24
26
  */
25
27
  import { spawnSync } from 'child_process';
28
+ import { randomUUID } from 'crypto';
29
+ import { mkdtempSync, readdirSync, rmSync, statSync } from 'fs';
30
+ import { tmpdir } from 'os';
31
+ import { join } from 'path';
32
+ import { isValidCloudflareArtifactsGitRemote, parseCanonicalCredentialFreeHttpsUrl, } from './cloudflare-artifacts-git-remote.js';
33
+ export const HOSTED_HARVEST_CA_BUNDLE_CANDIDATES = [
34
+ '/etc/pki/tls/certs/ca-bundle.crt',
35
+ '/etc/ssl/certs/ca-certificates.crt',
36
+ '/etc/ssl/cert.pem',
37
+ ];
38
+ export const HOSTED_HARVEST_CA_PATH_CANDIDATES = [
39
+ '/etc/pki/ca-trust/extracted/pem/directory-hash',
40
+ '/etc/ssl/certs',
41
+ ];
42
+ export const HOSTED_HARVEST_TRANSPORT_POLICY = {
43
+ environmentDenylist: [
44
+ 'HTTP_PROXY', 'http_proxy', 'HTTPS_PROXY', 'https_proxy', 'ALL_PROXY', 'all_proxy',
45
+ 'GIT_SSL_NO_VERIFY', 'GIT_PROXY_COMMAND', 'GIT_SSL_CAINFO', 'GIT_SSL_CAPATH',
46
+ 'CURL_CA_BUNDLE', 'SSL_CERT_FILE', 'SSL_CERT_DIR', 'GIT_SSL_CERT', 'GIT_SSL_KEY',
47
+ 'GIT_ASKPASS', 'GIT_ASKPASS_REQUIRE', 'SSH_ASKPASS', 'SSH_ASKPASS_REQUIRE',
48
+ 'GIT_TERMINAL_PROMPT',
49
+ ],
50
+ exactUrlHttpConfig: [
51
+ ['proxy', ''], ['sslVerify', 'true'], ['curloptResolve', ''],
52
+ ['followRedirects', 'false'],
53
+ ],
54
+ rejectedLocalHttpKeys: [
55
+ 'sslcert', 'sslkey', 'sslcertpasswordprotected', 'sslbackend',
56
+ 'pinnedpubkey', 'sslversion', 'sslcipherlist',
57
+ ],
58
+ };
59
+ function isRegularFile(path) {
60
+ try {
61
+ return statSync(path).isFile();
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ export function resolveHostedHarvestCaBundle(candidates = HOSTED_HARVEST_CA_BUNDLE_CANDIDATES) {
68
+ return candidates.find(isRegularFile) ?? null;
69
+ }
70
+ function resolveHostedHarvestCaPath(candidates = HOSTED_HARVEST_CA_PATH_CANDIDATES) {
71
+ for (const candidate of candidates) {
72
+ try {
73
+ if (!statSync(candidate).isDirectory())
74
+ continue;
75
+ if (readdirSync(candidate).some(entry => /^[0-9a-f]{8}\.[0-9]+$/u.test(entry)))
76
+ return candidate;
77
+ }
78
+ catch {
79
+ // A missing or unreadable directory is not a trust source.
80
+ }
81
+ }
82
+ return null;
83
+ }
26
84
  function defaultHarvestExec(workspaceDir) {
27
85
  return (args) => {
28
- const res = spawnSync('git', args, { cwd: workspaceDir, encoding: 'utf8' });
86
+ const env = { ...process.env };
87
+ for (const name of Object.keys(env)) {
88
+ if (HOSTED_HARVEST_TRANSPORT_POLICY.environmentDenylist.includes(name) ||
89
+ name === 'GIT_CONFIG' || name.startsWith('GIT_CONFIG_'))
90
+ delete env[name];
91
+ }
92
+ env.GIT_CONFIG_NOSYSTEM = '1';
93
+ env.GIT_CONFIG_GLOBAL = '/dev/null';
94
+ env.GIT_TERMINAL_PROMPT = '0';
95
+ const res = spawnSync('git', [...args], { cwd: workspaceDir, encoding: 'utf8', env });
29
96
  if (res.error) {
30
97
  return { exitCode: 1, stdout: '', stderr: res.error.message };
31
98
  }
32
99
  return { exitCode: res.status ?? 1, stdout: res.stdout ?? '', stderr: res.stderr ?? '' };
33
100
  };
34
101
  }
102
+ function protectPinnedRemote(exec, remote, caBundle, caPath, credentialHelper) {
103
+ const httpConfig = HOSTED_HARVEST_TRANSPORT_POLICY.exactUrlHttpConfig.flatMap(([key, value]) => ['-c', `http.${remote}.${key}=${value}`]);
104
+ const credentialUsername = new URL(remote).hostname === 'github.com' ? 'x-access-token' : 'x';
105
+ const credentialConfig = credentialHelper ? [
106
+ // An empty helper resets Git's accumulated helper list, including URL-scoped entries.
107
+ '-c', 'credential.helper=',
108
+ '-c', `credential.helper=${credentialHelper}`,
109
+ '-c', 'credential.useHttpPath=true',
110
+ '-c', `credential.${remote}.useHttpPath=true`,
111
+ '-c', `credential.username=${credentialUsername}`,
112
+ '-c', `credential.${remote}.username=${credentialUsername}`,
113
+ '-c', 'credential.interactive=false',
114
+ '-c', 'core.askPass=',
115
+ ] : ['-c', 'credential.helper='];
116
+ return (args) => {
117
+ const alias = `https://orizu-harvest.invalid/${randomUUID()}`;
118
+ const temporaryCaPath = caPath ? null : mkdtempSync(join(tmpdir(), 'orizu-harvest-ca-'));
119
+ try {
120
+ return exec([
121
+ '-c', `url.${remote}.insteadOf=${alias}`,
122
+ '-c', `url.${remote}.pushInsteadOf=${alias}`,
123
+ ...httpConfig,
124
+ '-c', `http.${remote}.sslCAInfo=${caBundle}`,
125
+ '-c', `http.${remote}.sslCAPath=${caPath ?? temporaryCaPath}`,
126
+ ...credentialConfig,
127
+ ...args.map(arg => arg === remote ? alias : arg),
128
+ ]);
129
+ }
130
+ finally {
131
+ if (temporaryCaPath)
132
+ rmSync(temporaryCaPath, { recursive: true, force: true });
133
+ }
134
+ };
135
+ }
35
136
  function detail(res) {
36
137
  const text = (res.stderr && res.stderr.trim()) || (res.stdout && res.stdout.trim()) || `exit ${res.exitCode}`;
37
138
  return text;
38
139
  }
140
+ const GITHUB_PATH = /^\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\.git$/u;
141
+ function isValidRepositoryRemote(remote) {
142
+ if (isValidCloudflareArtifactsGitRemote(remote))
143
+ return true;
144
+ const parsed = parseCanonicalCredentialFreeHttpsUrl(remote);
145
+ return parsed !== null && parsed.hostname === 'github.com' && GITHUB_PATH.test(parsed.pathname);
146
+ }
39
147
  function splitLines(text) {
40
148
  return text
41
149
  .split('\n')
@@ -43,16 +151,20 @@ function splitLines(text) {
43
151
  .filter(line => line.length > 0);
44
152
  }
45
153
  /**
46
- * Count commits present locally but not on the upstream tracking branch. Returns
47
- * 0 when there is no upstream (`@{u}` unresolvable) — with no upstream a push
48
- * could not land anyway, so "nothing to push" is the safe reading.
154
+ * Count commits present locally but not on the private ref fetched from the
155
+ * pinned repository. Exit-zero empty output is zero; lookup failures are unknown.
49
156
  */
50
- function countUnpushed(exec) {
51
- const res = exec(['rev-list', '--count', '@{u}..HEAD']);
157
+ function countUnpushed(exec, pinnedRef) {
158
+ const res = exec(['rev-list', '--count', `${pinnedRef}..HEAD`]);
52
159
  if (res.exitCode !== 0)
160
+ return null;
161
+ const output = res.stdout.trim();
162
+ if (output === '')
53
163
  return 0;
54
- const n = Number.parseInt(res.stdout.trim(), 10);
55
- return Number.isFinite(n) ? n : 0;
164
+ if (!/^\d+$/u.test(output))
165
+ return null;
166
+ const count = Number(output);
167
+ return Number.isSafeInteger(count) ? count : null;
56
168
  }
57
169
  /**
58
170
  * Persist any uncommitted or unpushed work in the workspace. Deterministic and
@@ -60,12 +172,58 @@ function countUnpushed(exec) {
60
172
  */
61
173
  export function harvestWorkspace(opts) {
62
174
  // Safety gate: never touch a real repo unless explicitly enabled or given an
63
- // injected exec. Absent both, report nothing-to-do rather than run host git.
175
+ // injected exec. Absent both, report that inspection was skipped.
64
176
  if (!opts.exec && !opts.enabled) {
65
- return { kind: 'work_none' };
177
+ return { kind: 'work_not_inspected' };
66
178
  }
67
- const exec = opts.exec ?? defaultHarvestExec(opts.workspaceDir);
179
+ if (!isValidRepositoryRemote(opts.repositoryRemote)) {
180
+ return { kind: 'work_persist_failed', error: 'repository_locator_invalid' };
181
+ }
182
+ // Every invocation gets a fresh, one-shot alias. Its exact command-scoped
183
+ // rewrite outranks repo-local prefix rules and resolves to the validated URL
184
+ // byte-for-byte; an observer of one argv cannot redirect a later command.
185
+ if (!opts.exec && (!opts.credentialHelper || /[\r\n\0]/u.test(opts.credentialHelper))) {
186
+ return { kind: 'work_persist_failed', error: 'checkpoint_credential_helper_invalid' };
187
+ }
188
+ const caBundleCandidates = opts.caBundleCandidates ?? HOSTED_HARVEST_CA_BUNDLE_CANDIDATES;
189
+ const caBundle = resolveHostedHarvestCaBundle(caBundleCandidates);
190
+ if (!caBundle) {
191
+ return {
192
+ kind: 'work_persist_failed',
193
+ error: `hosted_harvest_ca_bundle_missing: ${caBundleCandidates.join(', ')}`,
194
+ };
195
+ }
196
+ const caPath = resolveHostedHarvestCaPath(opts.caPathCandidates);
197
+ const rawExec = opts.exec ?? defaultHarvestExec(opts.workspaceDir);
198
+ const exec = protectPinnedRemote(rawExec, opts.repositoryRemote, caBundle, caPath, opts.credentialHelper);
199
+ const pinnedRef = `refs/orizu/pinned/${opts.sessionBranch}`;
68
200
  try {
201
+ const rejectedKeyPattern = HOSTED_HARVEST_TRANSPORT_POLICY.rejectedLocalHttpKeys.join('|');
202
+ const localTransportConfig = exec([
203
+ 'config', '--includes', '--show-origin', '--name-only', '--get-regexp',
204
+ `^http\\.(.*\\.)?(${rejectedKeyPattern})$`,
205
+ ]);
206
+ if (localTransportConfig.exitCode !== 0 && localTransportConfig.exitCode !== 1) {
207
+ return { kind: 'work_persist_failed', error: `git config inspection failed: ${detail(localTransportConfig)}` };
208
+ }
209
+ if (splitLines(localTransportConfig.stdout).length > 0) {
210
+ return { kind: 'work_persist_failed', error: 'hosted_harvest_transport_config_present' };
211
+ }
212
+ if (!opts.exec) {
213
+ const effectiveSslVerify = exec([
214
+ 'config', '--get-urlmatch', 'http.sslVerify', `${opts.repositoryRemote}/`,
215
+ ]);
216
+ if (effectiveSslVerify.exitCode !== 0 || effectiveSslVerify.stdout.trim() !== 'true') {
217
+ return { kind: 'work_persist_failed', error: 'hosted_harvest_tls_policy_ineffective' };
218
+ }
219
+ const effectiveCaPath = exec(['config', '--get-urlmatch', 'http.sslCAPath', `${opts.repositoryRemote}/`]);
220
+ const resolvedCaPath = effectiveCaPath.stdout.trim();
221
+ const expectedTemporaryPrefix = join(tmpdir(), 'orizu-harvest-ca-');
222
+ const isExpectedCaPath = caPath ? resolvedCaPath === caPath : resolvedCaPath.startsWith(expectedTemporaryPrefix);
223
+ if (effectiveCaPath.exitCode !== 0 || !isExpectedCaPath) {
224
+ return { kind: 'work_persist_failed', error: 'hosted_harvest_ca_path_policy_ineffective' };
225
+ }
226
+ }
69
227
  // Never harvest bootstrap-injected runtime scaffolding (ALI-1051): the
70
228
  // .claude/skills symlink is a sandbox-local pointer, not the agent's work.
71
229
  // Bootstrap also excludes it via .git/info/exclude; this pathspec is the
@@ -86,6 +244,12 @@ export function harvestWorkspace(opts) {
86
244
  `user.name=${opts.author.name}`,
87
245
  '-c',
88
246
  `user.email=${opts.author.email}`,
247
+ '-c',
248
+ 'core.hooksPath=/dev/null',
249
+ '-c',
250
+ 'commit.gpgsign=false',
251
+ '-c',
252
+ 'gpg.program=',
89
253
  'commit',
90
254
  '--author',
91
255
  `${opts.author.name} <${opts.author.email}>`,
@@ -96,23 +260,53 @@ export function harvestWorkspace(opts) {
96
260
  return { kind: 'work_persist_failed', error: `git commit failed: ${detail(commit)}` };
97
261
  }
98
262
  }
99
- // Nothing to push when the tree was clean AND there are no local-only commits.
100
- const unpushed = countUnpushed(exec);
101
- if (!dirty && unpushed <= 0) {
263
+ // Never derive checkpoint truth from mutable `origin`. Refresh a private ref
264
+ // from the independently validated repository before deciding there is no work.
265
+ const fetchPinned = exec([
266
+ 'fetch', '--no-tags', '--force', opts.repositoryRemote,
267
+ `+refs/heads/${opts.sessionBranch}:${pinnedRef}`,
268
+ ]);
269
+ const hasPinnedBase = fetchPinned.exitCode === 0;
270
+ const unpushed = hasPinnedBase ? countUnpushed(exec, pinnedRef) : null;
271
+ // Unknown is not none. Preserve round-3 behavior by attempting the exact
272
+ // pinned-repository push; omit file claims if no trustworthy base exists.
273
+ if (!dirty && unpushed === 0)
102
274
  return { kind: 'work_none' };
103
- }
104
275
  const headRes = exec(['rev-parse', 'HEAD']);
105
276
  const sha = headRes.exitCode === 0 ? headRes.stdout.trim() : '';
106
- // Files: the checkpoint commit's tree when we just committed, else everything
107
- // spanned by the unpushed commits.
108
- const filesRes = dirty
109
- ? exec(['diff-tree', '--no-commit-id', '--name-only', '-r', 'HEAD'])
110
- : exec(['diff', '--name-only', '@{u}..HEAD']);
111
- const files = filesRes.exitCode === 0 ? splitLines(filesRes.stdout) : [];
112
- const push = exec(['push']);
277
+ if (!/^[a-f0-9]{40}$/.test(sha)) {
278
+ return { kind: 'work_persist_failed', error: `git rev-parse HEAD failed: ${detail(headRes)}` };
279
+ }
280
+ // Files are the complete delta the exact push delivers, including any local
281
+ // commits the agent made before the final auto-harvest commit.
282
+ const filesRes = hasPinnedBase
283
+ ? exec(['diff', '--name-only', `${pinnedRef}..HEAD`])
284
+ : null;
285
+ const files = filesRes?.exitCode === 0 ? splitLines(filesRes.stdout) : null;
286
+ // --no-verify blocks hostile hooks, including Git LFS's legitimate upload
287
+ // hook. Run the equivalent upload explicitly before publishing the pointer ref.
288
+ const lfsVersion = exec(['lfs', 'version']);
289
+ if (lfsVersion.exitCode === 0) {
290
+ const lfsFiles = exec(['lfs', 'ls-files', '--name-only']);
291
+ if (lfsFiles.exitCode !== 0) {
292
+ return { kind: 'work_persist_failed', error: `git_lfs_inspection_failed: ${detail(lfsFiles)}` };
293
+ }
294
+ if (splitLines(lfsFiles.stdout).length > 0) {
295
+ const lfsPush = exec(['lfs', 'push', opts.repositoryRemote, sha]);
296
+ if (lfsPush.exitCode !== 0) {
297
+ return { kind: 'work_persist_failed', error: `git_lfs_push_failed: ${detail(lfsPush)}` };
298
+ }
299
+ }
300
+ }
301
+ // Push the exact object we report to the exact session destination. This
302
+ // avoids separately deriving a local claim and a tracking-branch update.
303
+ const push = exec([
304
+ 'push', '--no-verify', opts.repositoryRemote, `${sha}:refs/heads/${opts.sessionBranch}`,
305
+ ]);
113
306
  if (push.exitCode !== 0) {
114
307
  return { kind: 'work_persist_failed', error: `git push failed: ${detail(push)}` };
115
308
  }
309
+ exec(['update-ref', pinnedRef, sha]);
116
310
  return { kind: 'work_persisted', sha, files };
117
311
  }
118
312
  catch (error) {
@@ -22,6 +22,7 @@
22
22
  * (`@/packages/cli/src/hosted-loop`) is unchanged.
23
23
  */
24
24
  import type { HarnessEvent } from './hosted-harness.js';
25
+ import type { HostedSessionOrigin } from './hosted-runtime-assets.js';
25
26
  /**
26
27
  * Default provider-qualified hosted model — the SINGLE source of truth
27
28
  * (ALI-1086). Imported by both the operator-path session CLI
@@ -56,9 +57,20 @@ export interface HostedLoopContext {
56
57
  taskFile: string;
57
58
  /** Directory the agent operates in (the cloned session-branch workspace). */
58
59
  workspaceDir: string;
60
+ /** Validated destination branch for the CLI-owned checkpoint push. */
61
+ sessionBranch: string;
62
+ /** Credential-free repository locator established by bootstrap. */
63
+ repositoryRemote: string;
64
+ /** Bootstrap-owned helper re-applied command-scoped during checkpoint transport. */
65
+ repositoryCredentialHelper?: string;
59
66
  /** Provider-qualified model, e.g. "anthropic/claude-opus-4-8". */
60
67
  model: string;
61
68
  reasoningEffort?: string;
69
+ /**
70
+ * Session entry surface (ALI-1867). Missing values fail safely to CLI's
71
+ * unattended behavior; only `hosted-web` enables human question custody.
72
+ */
73
+ sessionOrigin?: HostedSessionOrigin;
62
74
  /** Idempotency / correlation key for the prompt. */
63
75
  messageId: string;
64
76
  /** Git identity for commit attribution during the prompt. */
@@ -143,14 +155,14 @@ export interface EgressProbeResult {
143
155
  detail: string;
144
156
  }
145
157
  /**
146
- * Default probe: attempt a bounded HTTPS GET to `host`. A live default-deny
147
- * firewall makes this THROW for a DENIED host (connection reset / timeout / DNS
158
+ * Default probe: attempt a bounded GET to a full URL target, or HTTPS when the
159
+ * target is a bare host. A live default-deny firewall makes this THROW for a DENIED host (connection reset / timeout / DNS
148
160
  * failure) → `reachable: false`. Any response at all — even an error status —
149
161
  * proves the host was reachable → `reachable: true`. Used for BOTH the allowed
150
162
  * (positive-control) and denied probes.
151
163
  */
152
164
  export declare function defaultProbeEgress(host: string): Promise<EgressProbeResult>;
153
- /** The two hosts the positive-control canary probes. */
165
+ /** The two host/URL targets the positive-control canary probes. */
154
166
  export interface EgressCanaryTargets {
155
167
  /** A known-ALLOWED host (the Orizu API base) that MUST be reachable — the
156
168
  * positive control that distinguishes "policy blocking" from "network dead". */
@@ -1 +1 @@
1
- {"version":3,"file":"hosted-loop-lifecycle.d.ts","sourceRoot":"","sources":["../src/hosted-loop-lifecycle.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hosted-loop-lifecycle.d.ts","sourceRoot":"","sources":["../src/hosted-loop-lifecycle.ts"],"names":[],"mappings":"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"}
@@ -68,8 +68,8 @@ export const DEFAULT_EGRESS_CANARY_HOST = 'example.com';
68
68
  * reachable host answers well within this. */
69
69
  const EGRESS_CANARY_TIMEOUT_MS = 5000;
70
70
  /**
71
- * Default probe: attempt a bounded HTTPS GET to `host`. A live default-deny
72
- * firewall makes this THROW for a DENIED host (connection reset / timeout / DNS
71
+ * Default probe: attempt a bounded GET to a full URL target, or HTTPS when the
72
+ * target is a bare host. A live default-deny firewall makes this THROW for a DENIED host (connection reset / timeout / DNS
73
73
  * failure) → `reachable: false`. Any response at all — even an error status —
74
74
  * proves the host was reachable → `reachable: true`. Used for BOTH the allowed
75
75
  * (positive-control) and denied probes.
@@ -78,7 +78,8 @@ export async function defaultProbeEgress(host) {
78
78
  const controller = new AbortController();
79
79
  const timer = setTimeout(() => controller.abort(), EGRESS_CANARY_TIMEOUT_MS);
80
80
  try {
81
- const response = await fetch(`https://${host}/`, {
81
+ const target = URL.canParse(host) ? new URL('/', host).toString() : `https://${host}/`;
82
+ const response = await fetch(target, {
82
83
  method: 'GET',
83
84
  redirect: 'manual',
84
85
  signal: controller.signal,
@@ -184,7 +185,8 @@ export async function runEgressCanary(targets, sink, probe, now = () => Date.now
184
185
  */
185
186
  export function egressCanaryAllowedHost(apiBaseUrl) {
186
187
  try {
187
- return new URL(apiBaseUrl).hostname;
188
+ const parsed = new URL(apiBaseUrl);
189
+ return parsed.port === '' ? parsed.hostname : parsed.origin;
188
190
  }
189
191
  catch {
190
192
  return apiBaseUrl.replace(/^[a-z]+:\/\//i, '').split('/')[0].split(':')[0];
@@ -16,7 +16,8 @@
16
16
  * driver; 'claude-agent-sdk': construct the in-process Claude-Agent-SDK
17
17
  * driver — no server to install or spawn), then drive the SINGLE task prompt
18
18
  * (v0: no queue) through `drainHarnessToSink`;
19
- * 5. the terminal event decides the run's final status via the sink's PATCH.
19
+ * 5. checkpoint dirty or local-only Git work without replacing the agent's
20
+ * terminal result, then deliver that original result through the sink.
20
21
  *
21
22
  * SWAPPABILITY (ALI-929 / P3.6): harness SELECTION is the ONLY thing that differs
22
23
  * between the two drivers — `start()` → `runPrompt()` → `drainHarnessToSink()` →
@@ -29,10 +30,11 @@
29
30
  * per-exec fallback — with firewall brokering the real key never reaches here).
30
31
  *
31
32
  * TESTABILITY: `runHostedLoop` takes injected `fetchImpl`, `spawnOpenCode`,
32
- * `installOpenCode`, and `createHarness`, so the whole loop runs in-process
33
- * against a fake broker + fake OpenCode server with no real binary.
33
+ * `installOpenCode`, `createHarness`, and Git execution, so the whole loop can
34
+ * run in-process against fake boundaries; separate tests exercise the real
35
+ * packaged CLI, HTTPS smart-Git transport, and OpenCode process boundary.
34
36
  */
35
- import type { AgentHarness } from './hosted-harness.js';
37
+ import { type AgentHarness } from './hosted-harness.js';
36
38
  import { type HarvestExec } from './hosted-harvest.js';
37
39
  import { type ModelValidationOutcome, type SpawnOpenCodeOptions, type SpawnedOpenCode } from './hosted-harness-opencode.js';
38
40
  import { type HostedFetch, type TerminalStatus } from './hosted-run-event-sink.js';
@@ -51,8 +53,35 @@ export interface InstallResult {
51
53
  ok: boolean;
52
54
  detail: string;
53
55
  }
56
+ interface HostedLoopSharedRuntime {
57
+ harness: AgentHarness | null;
58
+ spawned: SpawnedOpenCode | null;
59
+ agentSessionId: string | null;
60
+ installOk: boolean;
61
+ startupComplete: boolean;
62
+ spawnLogPath: string;
63
+ closed: boolean;
64
+ }
65
+ export interface HostedLoopSessionContext extends Omit<HostedLoopContext, 'runId' | 'bearerFile' | 'taskFile' | 'messageId'> {
66
+ sessionId: string;
67
+ projectId: string | null;
68
+ sessionDir: string;
69
+ }
70
+ export interface HostedLoopTurnContext {
71
+ runId: string;
72
+ ordinal: number;
73
+ taskFile: string;
74
+ messageId: string;
75
+ bearerFile: string;
76
+ }
77
+ export interface HostedLoopSessionHandle {
78
+ context: HostedLoopSessionContext;
79
+ runtime: HostedLoopSharedRuntime;
80
+ }
54
81
  export interface RunHostedLoopOptions {
55
82
  context: HostedLoopContext;
83
+ /** Internal session runtime used by the public session/turn lifecycle. */
84
+ sharedRuntime?: HostedLoopSharedRuntime;
56
85
  /**
57
86
  * Agent bearer, as a FIXED string (tests / in-process launcher) OR omitted in
58
87
  * favor of `bearerProvider`. In production the loop reads the rotated 0600
@@ -91,6 +120,9 @@ export interface RunHostedLoopOptions {
91
120
  }) => Promise<ModelValidationOutcome>;
92
121
  now?: () => number;
93
122
  signal?: AbortSignal;
123
+ /** Prompt-only human interrupt. Unlike `signal`, this never controls harness
124
+ * provisioning or the session-scoped runtime. */
125
+ interruptSignal?: AbortSignal;
94
126
  /** Extra verbatim secrets to redact (the bearer is added by the sink itself). */
95
127
  redactSecretsList?: readonly string[];
96
128
  /** Egress-canary probe (default: a bounded `fetch` to https://<host>/).
@@ -104,9 +136,9 @@ export interface RunHostedLoopOptions {
104
136
  workspaceDir: string;
105
137
  }) => Promise<SetupHookOutcome> | SetupHookOutcome;
106
138
  /**
107
- * Standing preamble wrapped around the user task (ALI-1036). Defaults to
108
- * `HOSTED_TASK_PREAMBLE`; pass an override to customize it, or an empty string
109
- * to send the task verbatim (tests). The user task is always kept verbatim
139
+ * Standing preamble wrapped around the user task (ALI-1036/ALI-1867). Defaults
140
+ * according to `context.sessionOrigin`; pass an override to customize it, or
141
+ * an empty string to send the task verbatim (tests). The task stays verbatim
110
142
  * beneath a delimiter (see `composeHostedTaskPrompt`).
111
143
  */
112
144
  taskPreamble?: string;
@@ -124,6 +156,9 @@ export interface RunHostedLoopOptions {
124
156
  /** Injectable sleep for the terminal-delivery retry backoff (ALI-1065
125
157
  * finding 2). Tests pass a no-op to avoid real delays. */
126
158
  sleepImpl?: (ms: number) => Promise<void>;
159
+ /** Value-free local diagnostics shared with the event sink. Production routes
160
+ * this through hosted-boot's stderr seam; consumer failures are ignored. */
161
+ onDiagnostic?: (message: string) => void;
127
162
  }
128
163
  export interface HostedLoopResult {
129
164
  status: TerminalStatus;
@@ -156,6 +191,11 @@ export declare const TERMINAL_DELIVERY_RETRY_DELAYS_MS: readonly number[];
156
191
  */
157
192
  export { INJECTED_ENV_VARS_ENV } from './hosted-environment.js';
158
193
  export declare function runHostedLoop(opts: RunHostedLoopOptions): Promise<HostedLoopResult>;
194
+ export declare function startHostedLoopSession(context: HostedLoopSessionContext): Promise<HostedLoopSessionHandle>;
195
+ export declare function runHostedLoopTurn(session: HostedLoopSessionHandle, turn: HostedLoopTurnContext, options: Omit<RunHostedLoopOptions, 'context' | 'taskPrompt' | 'sharedRuntime'> & {
196
+ taskPrompt: string;
197
+ }): Promise<HostedLoopResult>;
198
+ export declare function closeHostedLoopSession(session: HostedLoopSessionHandle): Promise<void>;
159
199
  export interface HostedLoopCommandIo {
160
200
  print: (line: string) => void;
161
201
  printErr?: (line: string) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAOH,OAAO,KAAK,EAAE,YAAY,EAAiB,MAAM,qBAAqB,CAAA;AAQtE,OAAO,EAAoB,KAAK,WAAW,EAAuB,MAAM,qBAAqB,CAAA;AAE7F,OAAO,EAML,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,KAAK,eAAe,EACrB,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EAKL,KAAK,WAAW,EAChB,KAAK,cAAc,EACpB,MAAM,4BAA4B,CAAA;AACnC,OAAO,EAOL,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EACtB,MAAM,4BAA4B,CAAA;AAKnC,OAAO,EACL,0BAA0B,EAC1B,oBAAoB,EACpB,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,GAChB,MAAM,4BAA4B,CAAA;AACnC,YAAY,EACV,oBAAoB,EACpB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,gBAAgB,GACjB,MAAM,4BAA4B,CAAA;AAOnC,eAAO,MAAM,wBAAwB,QAAgB,CAAA;AAErD;;;;;GAKG;AACH,wBAAgB,yBAAyB,CAAC,eAAe,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAKrF;AAED,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,OAAO,CAAA;IACX,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,iBAAiB,CAAA;IAC1B;;;;;OAKG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;yEACqE;IACrE,cAAc,CAAC,EAAE,MAAM,MAAM,CAAA;IAC7B,8EAA8E;IAC9E,UAAU,EAAE,MAAM,CAAA;IAClB,SAAS,CAAC,EAAE,WAAW,CAAA;IACvB;2FACuF;IACvF,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,mBAAmB,CAAC,EAAE,MAAM,CAAA;KAAE,KAAK,YAAY,CAAA;IAC7F;gFAC4E;IAC5E,mBAAmB,CAAC,EAAE,MAAM,YAAY,CAAA;IACxC,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,eAAe,GAAG,OAAO,CAAC,eAAe,CAAC,CAAA;IAC1F,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,aAAa,CAAC,CAAA;IAC9C;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,sBAAsB,CAAC,CAAA;IAC9F,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;IACrC;;kFAE8E;IAC9E,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAA;IAC1D;;oDAEgD;IAChD,YAAY,CAAC,EAAE,CAAC,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,KAAK,OAAO,CAAC,gBAAgB,CAAC,GAAG,gBAAgB,CAAA;IAChG;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB;;;;OAIG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;+DAC2D;IAC3D,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1C;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,cAAc,CAAA;IACtB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,SAAS,EAAE,OAAO,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAeD;;;;;iDAKiD;AACjD,eAAO,MAAM,6BAA6B,QAAiB,CAAA;AAa3D;;;;;;;GAOG;AACH,eAAO,MAAM,iCAAiC,EAAE,SAAS,MAAM,EAAmB,CAAA;AAKlF;;;;;;GAMG;AACH,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAA;AA4E/D,wBAAsB,aAAa,CAAC,IAAI,EAAE,oBAAoB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAgZzF;AAID,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC7B,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACjC,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AASD;;;;GAIG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,EAAE,EAAE,mBAAmB,GACtB,OAAO,CAAC,MAAM,CAAC,CAwBjB"}
1
+ {"version":3,"file":"hosted-loop.d.ts","sourceRoot":"","sources":["../src/hosted-loop.ts"],"names":[],"mappings":"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"}