orizu 0.5.44 → 0.5.46

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.
@@ -1 +1 @@
1
- {"version":3,"file":"account-directory.d.ts","sourceRoot":"","sources":["../src/account-directory.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"account-directory.d.ts","sourceRoot":"","sources":["../src/account-directory.ts"],"names":[],"mappings":"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"}
@@ -357,20 +357,13 @@ async function addTeamMember() {
357
357
  if (!email) {
358
358
  throw new Error('Usage: orizu teams members add --email <email> [--team <teamSlug>]');
359
359
  }
360
- const response = await authedFetch(`/api/cli/teams/${encodeURIComponent(teamSlug)}/members`, {
361
- method: 'POST',
362
- headers: { 'Content-Type': 'application/json' },
363
- body: JSON.stringify({ email }),
364
- });
365
- if (!response.ok) {
366
- throw new Error(`Failed to add team member: ${await response.text()}`);
367
- }
368
- const data = await parseJsonResponse(response, 'Team member add');
360
+ const { addTeamMemberWithDeadline } = await import('./team-member-add-workflow.js');
361
+ const member = await addTeamMemberWithDeadline(teamSlug, email, authedFetch);
369
362
  if (hasJsonFlag()) {
370
- printJson({ member: data.member });
363
+ printJson({ member });
371
364
  return;
372
365
  }
373
- printLine(`Added team member ${sanitizeTerminalText(data.member.email)} (${sanitizeTerminalText(data.member.id)})`);
366
+ printLine(`Added team member ${sanitizeTerminalText(member.email)} (${sanitizeTerminalText(member.id)})`);
374
367
  }
375
368
  async function removeTeamMember() {
376
369
  const teamSlug = await resolveTeamSlug(getArg('--team'));
@@ -160,6 +160,9 @@ export interface ResolvedSession {
160
160
  agentSessionId: string | null;
161
161
  seedOrigin: 'team' | 'session';
162
162
  projectId: string | null;
163
+ /** The session's own project's slug and name (ORI-2609), when the server sends them. */
164
+ projectSlug: string | null;
165
+ projectName: string | null;
163
166
  /** Session lifetime in minutes (from client_info), if the coordinator recorded
164
167
  * it — used to derive the per-prompt max-duration cap (ALI-1061). */
165
168
  durationMinutes: number | null;
@@ -1 +1 @@
1
- {"version":3,"file":"hosted-boot.d.ts","sourceRoot":"","sources":["../src/hosted-boot.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"hosted-boot.d.ts","sourceRoot":"","sources":["../src/hosted-boot.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,7 @@ import { INJECTED_ENV_VARS_ENV, closeHostedLoopSession, runHostedLoop, runHosted
39
39
  import { DEFAULT_EGRESS_CANARY_HOST, DEFAULT_HOSTED_MODEL } from './hosted-loop-lifecycle.js';
40
40
  import { composeHostedAnswerPrompt } from './hosted-question.js';
41
41
  import { resumeRunEventSink } from './hosted-run-event-sink.js';
42
+ import { seedProjectFolder } from './hosted-project-folder-seed.js';
42
43
  import { stageOrizuSkill } from './hosted-skill-staging.js';
43
44
  import { openHostedLiveChannel } from './hosted-live-channel.js';
44
45
  /** Non-secret placeholder model key (firewall brokers the real org key on
@@ -381,6 +382,8 @@ export async function resolveSession(opts) {
381
382
  // A server with copying switched off omits seedOrigin; no source is bound.
382
383
  seedOrigin: session.seedOrigin === 'session' ? 'session' : 'team',
383
384
  projectId: asString(session.projectId),
385
+ projectSlug: asString(session.projectSlug),
386
+ projectName: asString(session.projectName),
384
387
  };
385
388
  }
386
389
  export const HOSTED_SESSION_DEFAULT_POLL_MS = 5_000;
@@ -1052,6 +1055,17 @@ const c = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
1052
1055
  exec('git', ['-C', workspaceDir, 'config', 'user.email', AGENT_GIT_IDENTITY.email]);
1053
1056
  if (!wokenDisk && !sessionCopyFilled)
1054
1057
  log(`cloned ${session.repoBranch}`);
1058
+ // 5a — ORI-2609: the team copy holds only the projects the team seed knew
1059
+ // about. Write this session's project folder if it is missing, before the
1060
+ // agent starts. Never fails the boot. A woken disk is the agent's own saved
1061
+ // state, so a folder it removed there is not put back.
1062
+ log(wokenDisk
1063
+ ? 'project folder left as the woken disk has it'
1064
+ : seedProjectFolder({
1065
+ workTree: workspaceDir,
1066
+ project: { id: session.projectId, slug: session.projectSlug, name: session.projectName },
1067
+ exec: (cmd, args) => exec(cmd, args),
1068
+ }));
1055
1069
  // 5b — Stage the orizu skill into the cloned repo so the agent discovers the
1056
1070
  // Orizu workflows (ALI-1059). SHARED with the operator path via `stageOrizuSkill`
1057
1071
  // (one resolution chain + the harvest-safe .git/info/exclude append). Non-fatal:
@@ -0,0 +1,18 @@
1
+ export interface ProjectFolderSeedProject {
2
+ id: string | null;
3
+ slug: string | null;
4
+ name: string | null;
5
+ }
6
+ type SeedExec = (cmd: string, args: string[]) => {
7
+ status: number;
8
+ stdout: string;
9
+ stderr: string;
10
+ };
11
+ /** Returns the one line the boot logs. */
12
+ export declare function seedProjectFolder(input: {
13
+ workTree: string;
14
+ project: ProjectFolderSeedProject;
15
+ exec: SeedExec;
16
+ }): string;
17
+ export {};
18
+ //# sourceMappingURL=hosted-project-folder-seed.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hosted-project-folder-seed.d.ts","sourceRoot":"","sources":["../src/hosted-project-folder-seed.ts"],"names":[],"mappings":"AAqBA,MAAM,WAAW,wBAAwB;IACvC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;IACjB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;IACnB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CACpB;AAED,KAAK,QAAQ,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAgBnG,0CAA0C;AAC1C,wBAAgB,iBAAiB,CAAC,KAAK,EAAE;IACvC,QAAQ,EAAE,MAAM,CAAA;IAChB,OAAO,EAAE,wBAAwB,CAAA;IACjC,IAAI,EAAE,QAAQ,CAAA;CACf,GAAG,MAAM,CA8CT"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * ORI-2609: every project of an approved team shares the team's repository,
3
+ * but the team seed wrote only the first project's folder. A hosted session
4
+ * for another project writes that project's folder into its own checkout at
5
+ * boot, exactly as the team seed would have, and commits it locally. The
6
+ * session's normal save carries the commit; nothing is pushed here.
7
+ *
8
+ * A folder that is already there, whole or partial, is never touched, so a
9
+ * second session never duplicates or overwrites one. Never throws: a seed
10
+ * failure is reported for the boot to log, and the session still starts.
11
+ */
12
+ import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
13
+ import { dirname, join } from 'node:path';
14
+ import { buildSeedContractFiles, SEED_COMMIT_AUTHOR_EMAIL, SEED_COMMIT_AUTHOR_NAME, SEED_COMMIT_DATE, } from './workspace-bootstrap-seed.js';
15
+ // One plain folder name: no separators, no traversal, no leading dot or dash.
16
+ const SAFE_PROJECT_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/;
17
+ function readTeamFacts(workTree) {
18
+ // The team manifest the team seed wrote is the source of the team facts, so
19
+ // the new project's manifest agrees with the repository it lands in.
20
+ const manifest = JSON.parse(readFileSync(join(workTree, 'orizu.team.json'), 'utf8'));
21
+ if (typeof manifest.slug !== 'string' || manifest.slug.length === 0) {
22
+ throw new Error('orizu.team.json carries no team slug');
23
+ }
24
+ // The team id does not appear in project files; it is passed for completeness.
25
+ return { id: typeof manifest.teamId === 'string' ? manifest.teamId : '', slug: manifest.slug };
26
+ }
27
+ /** Returns the one line the boot logs. */
28
+ export function seedProjectFolder(input) {
29
+ const { workTree, project, exec } = input;
30
+ if (!project.id)
31
+ return 'project folder skipped: the session names no project';
32
+ // Without the slug there is no folder name; the server leaves it out when it
33
+ // could not read the project.
34
+ if (!project.slug)
35
+ return 'project folder skipped: the server sent no project slug';
36
+ try {
37
+ const slug = project.slug;
38
+ if (!SAFE_PROJECT_SLUG.test(slug))
39
+ throw new Error('the project slug is not a plain folder name');
40
+ const folder = `projects/${slug}`;
41
+ const projectsDir = join(workTree, 'projects');
42
+ if (existsSync(projectsDir) && !lstatSync(projectsDir).isDirectory()) {
43
+ throw new Error('projects/ is not a folder');
44
+ }
45
+ if (existsSync(join(workTree, folder)))
46
+ return `project folder ${folder}/ already present`;
47
+ // Byte-for-byte the files the team seed writes for this project.
48
+ const files = buildSeedContractFiles({
49
+ team: readTeamFacts(workTree),
50
+ projects: [{ id: project.id, slug, name: project.name }],
51
+ }).filter(file => file.path.startsWith(`${folder}/`));
52
+ for (const file of files) {
53
+ const target = join(workTree, file.path);
54
+ mkdirSync(dirname(target), { recursive: true });
55
+ writeFileSync(target, file.bytes, { mode: 0o644 });
56
+ }
57
+ // No hook the checkout's config names runs inside the boot (as in harvest).
58
+ // The exec seam takes no `env` option, so `env` pins the commit dates.
59
+ const git = (step, ...args) => {
60
+ const result = exec('env', [
61
+ `GIT_AUTHOR_DATE=${SEED_COMMIT_DATE}`, `GIT_COMMITTER_DATE=${SEED_COMMIT_DATE}`,
62
+ 'git', '-C', workTree, '-c', 'core.hooksPath=/dev/null', '-c', 'core.fsmonitor=false', ...args,
63
+ ]);
64
+ if (result.status !== 0)
65
+ throw new Error(`git ${step} failed: ${(result.stderr || result.stdout).trim()}`);
66
+ };
67
+ git('add', 'add', '--', folder);
68
+ // Paths after `--` commit only this folder, whatever else is staged. A fixed
69
+ // author, committer and date make every session that starts from the same
70
+ // team head produce the same commit, so two sessions' publishes share it
71
+ // rather than conflict on files neither of them touched.
72
+ git('commit', '-c', 'commit.gpgsign=false', '-c', `user.name=${SEED_COMMIT_AUTHOR_NAME}`, '-c', `user.email=${SEED_COMMIT_AUTHOR_EMAIL}`, 'commit', '--quiet', '--no-verify', '-m', `Add the ${slug} project folder`, '--', folder);
73
+ return `project folder ${folder}/ seeded`;
74
+ }
75
+ catch (error) {
76
+ return `project folder seed failed: ${error instanceof Error ? error.message : String(error)}`;
77
+ }
78
+ }
@@ -28,6 +28,20 @@
28
28
  * event payload can be passed through before it is sent to the RunAPI.
29
29
  */
30
30
  export declare const REDACTION_PLACEHOLDER = "[redacted]";
31
+ /**
32
+ * The shape pass alone, for servers that hold no secret values: the web app
33
+ * and the session coordinator scrub agent text with it before it reaches a
34
+ * browser (ORI-1905). One list of shapes for the CLI and both servers.
35
+ */
36
+ export declare function redactSecretShapes(input: string): string;
37
+ /**
38
+ * For reply text shown as it is written: each snapshot is the reply so far, so
39
+ * one can end part-way through a token, before it is long enough for its shape
40
+ * to match. Such a tail is redacted too. The saved read shows a reply's last
41
+ * snapshot, so a finished reply that really ends in a bare `sk-` loses those
42
+ * characters there as well.
43
+ */
44
+ export declare function redactStreamedTextShapes(input: string): string;
31
45
  export interface RedactOptions {
32
46
  /** Verbatim secret values to strip wherever they appear (bearer, tokens). */
33
47
  secrets?: readonly string[];
@@ -1 +1 @@
1
- {"version":3,"file":"secret-redaction.d.ts","sourceRoot":"","sources":["../src/secret-redaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,eAAO,MAAM,qBAAqB,eAAe,CAAA;AAgDjD,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAE,aAAkB,GAAG,CAAC,CAGzE;AAmBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAQjF"}
1
+ {"version":3,"file":"secret-redaction.d.ts","sourceRoot":"","sources":["../src/secret-redaction.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,eAAO,MAAM,qBAAqB,eAAe,CAAA;AAgDjD;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAUD;;;;;;GAMG;AACH,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI9D;AAED,MAAM,WAAW,aAAa;IAC5B,6EAA6E;IAC7E,OAAO,CAAC,EAAE,SAAS,MAAM,EAAE,CAAA;CAC5B;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,GAAE,aAAkB,GAAG,CAAC,CAGzE;AAmBD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,OAAO,CAQjF"}
@@ -70,6 +70,31 @@ function redactString(input, secrets) {
70
70
  }
71
71
  return out;
72
72
  }
73
+ /**
74
+ * The shape pass alone, for servers that hold no secret values: the web app
75
+ * and the session coordinator scrub agent text with it before it reaches a
76
+ * browser (ORI-1905). One list of shapes for the CLI and both servers.
77
+ */
78
+ export function redactSecretShapes(input) {
79
+ return redactString(input, []);
80
+ }
81
+ // A known credential prefix at the end of the text, followed by fewer
82
+ // characters than its shape above needs: the start of a token still being
83
+ // written. Each run is bounded, so the scan stays linear in the text, and a
84
+ // longer run that is not a token (a path, a word) is left alone. `Bearer` only
85
+ // counts in an Authorization header, so prose like "a Bearer token" keeps its
86
+ // last word.
87
+ const OPEN_TOKEN_AT_END = /(?:\bgithub_pat_[A-Za-z0-9_]{0,19}|\bgh[posur]_[A-Za-z0-9_]{0,19}|\borizu_(?:agent|pat)_[A-Za-z0-9_-]{0,9}|\bsk-[A-Za-z0-9_-]{0,19}|\b(authorization:\s*bearer)\s+[A-Za-z0-9._~+/-]{0,15})$/i;
88
+ /**
89
+ * For reply text shown as it is written: each snapshot is the reply so far, so
90
+ * one can end part-way through a token, before it is long enough for its shape
91
+ * to match. Such a tail is redacted too. The saved read shows a reply's last
92
+ * snapshot, so a finished reply that really ends in a bare `sk-` loses those
93
+ * characters there as well.
94
+ */
95
+ export function redactStreamedTextShapes(input) {
96
+ return redactSecretShapes(input).replace(OPEN_TOKEN_AT_END, (_match, header) => header ? `${header} ${REDACTION_PLACEHOLDER}` : REDACTION_PLACEHOLDER);
97
+ }
73
98
  /**
74
99
  * Deep-redact a JSON-ish value. Strings are scrubbed; arrays/objects are walked;
75
100
  * everything else is returned as-is. Object KEYS are never treated as secrets
@@ -23,6 +23,11 @@ export interface SkilledProposerChildOutput {
23
23
  stdout: (chunk: string) => void;
24
24
  stderr: (chunk: string) => void;
25
25
  }
26
+ /**
27
+ * Run the optimizer, passing a stop signal on to it. On a stop signal the CLI
28
+ * waits until the optimizer exits (capped at CHILD_STOP_WAIT_MS, then kills
29
+ * it) and then ends by that same signal.
30
+ */
26
31
  export declare function spawnSkilledProposerChild(command: string, args: string[], engine: GepaEngine, environment: NodeJS.ProcessEnv, onSpawn?: () => Promise<void>, output?: SkilledProposerChildOutput): Promise<{
27
32
  status: number | null;
28
33
  error?: Error;
@@ -1 +1 @@
1
- {"version":3,"file":"skilled-proposer-launch.d.ts","sourceRoot":"","sources":["../src/skilled-proposer-launch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAO3D,UAAU,yBAAyB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,oBAAoB,EAAE,OAAO,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAClC;AACD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAC,UAAU,CAAA;IAC9B,MAAM,CAAC,EAAE,yBAAyB,CAAA;CACnC;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAiBD,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU,EACnF,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7B,MAAM,CAAC,EAAE,0BAA0B;;;GA4DpC;AAwCD,UAAU,aAAa;IAAG,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE;AAQ9F,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,UAAQ,GAAG,MAAM,CAepF;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,qBAAqB,CAyBvB"}
1
+ {"version":3,"file":"skilled-proposer-launch.d.ts","sourceRoot":"","sources":["../src/skilled-proposer-launch.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAA;AAO3D,UAAU,yBAAyB;IACjC,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,EAAE,MAAM,CAAA;IAClB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,iBAAiB,EAAE,MAAM,EAAE,CAAA;IAC3B,QAAQ,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAClD,oBAAoB,EAAE,OAAO,CAAA;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,aAAa,CAAC,EAAE,OAAO,GAAG,IAAI,CAAA;IAC9B,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,IAAI,CAAA;CAClC;AACD,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd,WAAW,EAAE,MAAM,CAAC,UAAU,CAAA;IAC9B,MAAM,CAAC,EAAE,yBAAyB,CAAA;CACnC;AAED,MAAM,WAAW,0BAA0B;IACzC,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAC/B,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAChC;AAsBD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,CAAC,UAAU,EACnF,OAAO,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,EAC7B,MAAM,CAAC,EAAE,0BAA0B;YA0CX,MAAM,GAAG,IAAI;YAAU,KAAK;GAkDrD;AAwCD,UAAU,aAAa;IAAG,KAAK,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;CAAE;AAQ9F,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,aAAa,EAAE,MAAM,UAAQ,GAAG,MAAM,CAepF;AAED,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,MAAM,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,MAAM,CAAC,UAAU,GAC7B,qBAAqB,CAyBvB"}
@@ -23,70 +23,128 @@ function materializeConfigPayload(engine, environment) {
23
23
  }
24
24
  return () => rmSync(root, { recursive: true, force: true });
25
25
  }
26
+ const STOP_SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];
27
+ // The optimizer marks its run stopped before exiting (at most 10 s for the
28
+ // status write); past this it is killed so nothing is left running (ORI-2402).
29
+ const CHILD_STOP_WAIT_MS = 15_000;
30
+ /**
31
+ * Run the optimizer, passing a stop signal on to it. On a stop signal the CLI
32
+ * waits until the optimizer exits (capped at CHILD_STOP_WAIT_MS, then kills
33
+ * it) and then ends by that same signal.
34
+ */
26
35
  export async function spawnSkilledProposerChild(command, args, engine, environment, onSpawn, output) {
27
36
  const childEnvironment = { ...environment };
28
- const shouldMaterialize = engine === 'official'
29
- && childEnvironment.ORIZU_CANDIDATE_PROPOSER === 'skilled-proposer'
30
- && childEnvironment.ORIZU_SKILLED_PROPOSER_CONFIG !== undefined;
31
- if (!shouldMaterialize)
32
- return spawnSync(command, args, { stdio: 'inherit', env: childEnvironment });
33
37
  let child;
34
38
  let cleanup = () => { };
35
- const signals = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'];
36
- const handlers = signals.map(signal => ({
37
- signal,
38
- handle: () => {
39
+ let stopSignal;
40
+ // On the terminal path the optimizer leads its own process group, so a stop
41
+ // also reaches its runners, which may run in worker threads it can't stop.
42
+ let childGroup;
43
+ const forward = (signal) => {
44
+ if (childGroup) {
39
45
  try {
40
- cleanup();
46
+ process.kill(-childGroup, signal);
47
+ return;
41
48
  }
42
- finally {
43
- try {
44
- child?.kill(signal);
45
- }
46
- finally {
47
- for (const entry of handlers)
48
- process.off(entry.signal, entry.handle);
49
- process.kill(process.pid, signal);
50
- }
49
+ catch { /* group already gone */ }
50
+ }
51
+ try {
52
+ child?.kill(signal);
53
+ }
54
+ catch { /* already exited */ }
55
+ };
56
+ let giveUp;
57
+ const removeHandlers = () => { for (const entry of handlers)
58
+ process.off(entry.signal, entry.handle); };
59
+ const endBySignal = (signal) => {
60
+ if (giveUp)
61
+ clearTimeout(giveUp);
62
+ removeHandlers();
63
+ cleanup();
64
+ // With our handlers gone, the signal's default action ends the process.
65
+ process.kill(process.pid, signal);
66
+ return new Promise(() => { });
67
+ };
68
+ const handlers = STOP_SIGNALS.map(signal => ({
69
+ signal,
70
+ handle: () => {
71
+ if (!child) {
72
+ void endBySignal(signal);
73
+ return;
51
74
  }
75
+ forward(signal);
76
+ if (stopSignal)
77
+ return;
78
+ stopSignal = signal;
79
+ // The child read the private payload at start and a stopping run
80
+ // proposes nothing, so it goes now rather than when the child exits.
81
+ cleanup();
82
+ giveUp = setTimeout(() => forward('SIGKILL'), CHILD_STOP_WAIT_MS);
52
83
  },
53
84
  }));
54
85
  for (const entry of handlers)
55
- process.once(entry.signal, entry.handle);
86
+ process.on(entry.signal, entry.handle);
56
87
  try {
57
88
  cleanup = materializeConfigPayload(engine, childEnvironment) ?? cleanup;
58
89
  await new Promise(resolve => setImmediate(resolve));
90
+ let result;
59
91
  if (output) {
60
- return await runPipedChild(command, args, childEnvironment, async () => { await onSpawn?.(); }, output, spawnedChild => { child = spawnedChild; });
61
- }
62
- const spawnedChild = spawn(command, args, {
63
- stdio: 'inherit',
64
- env: childEnvironment,
65
- });
66
- child = spawnedChild;
67
- const spawned = new Promise((resolve, reject) => {
68
- spawnedChild.once('spawn', resolve);
69
- spawnedChild.once('error', reject);
70
- });
71
- const closed = new Promise(resolve => {
72
- let error;
73
- spawnedChild.once('error', cause => { error = cause; });
74
- spawnedChild.once('close', status => resolve(error ? { status, error } : { status }));
75
- });
76
- await spawned;
77
- try {
78
- await onSpawn?.();
92
+ result = await runPipedChild(command, args, childEnvironment, async () => { await onSpawn?.(); }, output, spawnedChild => { child = spawnedChild; });
79
93
  }
80
- catch (error) {
81
- spawnedChild.kill('SIGTERM');
82
- await closed;
83
- throw error;
94
+ else {
95
+ // stdin is not passed on: a process outside the terminal's foreground
96
+ // group that read the terminal would be stopped by it.
97
+ // Outside the CLI's group, the optimizer would outlive a SIGKILL of the
98
+ // CLI; it watches for that and stops itself.
99
+ const spawnedChild = spawn(command, args, {
100
+ stdio: ['ignore', 'inherit', 'inherit'],
101
+ env: { ...childEnvironment, ORIZU_STOP_WITH_PARENT: '1' },
102
+ detached: true,
103
+ });
104
+ child = spawnedChild;
105
+ childGroup = spawnedChild.pid;
106
+ const spawned = new Promise((resolve, reject) => {
107
+ spawnedChild.once('spawn', resolve);
108
+ spawnedChild.once('error', reject);
109
+ });
110
+ const closed = new Promise(resolve => {
111
+ let error;
112
+ spawnedChild.once('error', cause => { error = cause; });
113
+ spawnedChild.once('close', status => resolve(error ? { status, error } : { status }));
114
+ });
115
+ // A launch failure (e.g. ENOENT) is returned, not thrown, so callers
116
+ // name the engine as they did with spawnSync.
117
+ try {
118
+ await spawned;
119
+ }
120
+ catch (error) {
121
+ return { status: null, error: error };
122
+ }
123
+ try {
124
+ await onSpawn?.();
125
+ }
126
+ catch (error) {
127
+ spawnedChild.kill('SIGTERM');
128
+ await closed;
129
+ throw error;
130
+ }
131
+ result = await closed;
132
+ // A runner that ignored the stop must not outlive the optimizer.
133
+ if (stopSignal) {
134
+ try {
135
+ process.kill(-spawnedChild.pid, 'SIGKILL');
136
+ }
137
+ catch { /* group already gone */ }
138
+ }
84
139
  }
85
- return await closed;
140
+ if (stopSignal)
141
+ return await endBySignal(stopSignal);
142
+ return result;
86
143
  }
87
144
  finally {
88
- for (const entry of handlers)
89
- process.off(entry.signal, entry.handle);
145
+ if (giveUp)
146
+ clearTimeout(giveUp);
147
+ removeHandlers();
90
148
  cleanup();
91
149
  }
92
150
  }
@@ -0,0 +1,15 @@
1
+ import { Schema } from 'effect';
2
+ declare const AddReply: Schema.Struct<{
3
+ readonly member: Schema.Struct<{
4
+ readonly id: Schema.NonEmptyString;
5
+ readonly email: Schema.String;
6
+ }>;
7
+ }>;
8
+ /** The member as the server sent it; `--json` prints every field it has. */
9
+ export type AddedMember = typeof AddReply.Type['member'] & Readonly<Record<string, unknown>>;
10
+ /** Sends one request; calls `onRequestStart` just before anything goes out. */
11
+ export type TeamMemberAddRequest = (path: string, init: RequestInit, onRequestStart?: () => void) => Promise<Response>;
12
+ /** `orizu teams members add`: the invited member, or an error saying whether the invite may be saved. */
13
+ export declare function addTeamMemberWithDeadline(teamSlug: string, email: string, request: TeamMemberAddRequest): Promise<AddedMember>;
14
+ export {};
15
+ //# sourceMappingURL=team-member-add-workflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"team-member-add-workflow.d.ts","sourceRoot":"","sources":["../src/team-member-add-workflow.ts"],"names":[],"mappings":"AAAA,OAAO,EAA+B,MAAM,EAAE,MAAM,QAAQ,CAAA;AAmB5D,QAAA,MAAM,QAAQ;;;;;EAKZ,CAAA;AAEF,4EAA4E;AAC5E,MAAM,MAAM,WAAW,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAA;AAE5F,+EAA+E;AAC/E,MAAM,MAAM,oBAAoB,GAAG,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,MAAM,IAAI,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAA;AA2EtH,yGAAyG;AACzG,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,WAAW,CAAC,CAMpI"}
@@ -0,0 +1,111 @@
1
+ import { Cause, Effect, Exit, Result, Schema } from 'effect';
2
+ import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js';
3
+ import { extractErrorMessage } from './error-response.js';
4
+ import { sanitizeTerminalText } from './json-response.js';
5
+ import { appErrorOf, unreachedCode } from './request-outcome.js';
6
+ // Loaded only through `await import(...)`, like the dataset workflows.
7
+ const FAILED = 'Failed to add team member';
8
+ // What the route says when it cannot tell whether the invite was saved (ORI-2383 slice A).
9
+ const SERVER_MAY_BE_SAVED = 'Invite may already be saved. Refresh the member list before inviting again.';
10
+ // The route saves the address trimmed and lowercased, so the check names that address.
11
+ const mayBeInvited = (teamSlug, email) => {
12
+ const saved = sanitizeTerminalText(email.trim().toLowerCase());
13
+ return `${saved} may already be invited: run \`orizu teams members list --team ${teamSlug}\` and look for ${saved}. If they are listed, they were invited; don't invite them again.`;
14
+ };
15
+ const AddReply = Schema.Struct({
16
+ member: Schema.Struct({
17
+ id: Schema.NonEmptyString,
18
+ email: Schema.String,
19
+ }),
20
+ });
21
+ // The invite may be saved when the server says so, when the reply was lost,
22
+ // late or unreadable after the request was sent, when a 5xx did not come from
23
+ // the app (a gateway giving up while the app kept working), or when the reply
24
+ // has no member. Any other error reply is a clear refusal.
25
+ class TeamMemberAddFailure extends Error {
26
+ mayBeSaved;
27
+ _tag = 'TeamMemberAddFailure';
28
+ constructor(message, mayBeSaved) {
29
+ super(message);
30
+ this.mayBeSaved = mayBeSaved;
31
+ }
32
+ }
33
+ // One request, never retried: a retry after a saved invite is refused as
34
+ // "already a member", which hides that the first one worked.
35
+ function memberAdd(teamSlug, email, request, timeoutMs) {
36
+ return Effect.suspend(() => {
37
+ let sent = false;
38
+ let timedOut = false;
39
+ return Effect.tryPromise({
40
+ try: async (signal) => {
41
+ const controller = new AbortController();
42
+ const abort = () => controller.abort();
43
+ const timer = setTimeout(() => { timedOut = true; controller.abort(); }, timeoutMs);
44
+ if (signal.aborted)
45
+ abort();
46
+ else
47
+ signal.addEventListener('abort', abort, { once: true });
48
+ try {
49
+ const response = await request(`/api/cli/teams/${encodeURIComponent(teamSlug)}/members`, {
50
+ method: 'POST',
51
+ headers: { 'Content-Type': 'application/json' },
52
+ body: JSON.stringify({ email }),
53
+ signal: controller.signal,
54
+ }, () => { sent = true; });
55
+ if (!response.ok) {
56
+ const appError = await appErrorOf(response.clone());
57
+ const detail = await extractErrorMessage(response);
58
+ if (appError === SERVER_MAY_BE_SAVED)
59
+ throw new TeamMemberAddFailure(appError, true);
60
+ if (response.status < 400 || (response.status >= 500 && appError === null)) {
61
+ throw new TeamMemberAddFailure(`Member add got no clear answer (status ${response.status}).`, true);
62
+ }
63
+ // The route's own failures already say what failed.
64
+ throw new TeamMemberAddFailure(detail.startsWith('Failed to ') ? detail : `${FAILED}: ${detail}`, false);
65
+ }
66
+ // Reading the body can still fail or stall; that stays under the deadline.
67
+ const body = await response.text();
68
+ let data;
69
+ try {
70
+ data = JSON.parse(body);
71
+ }
72
+ catch {
73
+ data = undefined;
74
+ }
75
+ if (Result.isFailure(Schema.decodeUnknownResult(AddReply)(data))) {
76
+ throw new TeamMemberAddFailure(`Member add returned an unexpected response (status ${response.status}).`, true);
77
+ }
78
+ // Decoding keeps only the named fields; the reply's member is returned whole.
79
+ return data.member;
80
+ }
81
+ finally {
82
+ clearTimeout(timer);
83
+ signal.removeEventListener('abort', abort);
84
+ controller.abort();
85
+ }
86
+ },
87
+ catch: cause => {
88
+ if (cause instanceof TeamMemberAddFailure)
89
+ return cause;
90
+ if (timedOut)
91
+ return new TeamMemberAddFailure('Member add exceeded its deadline.', sent);
92
+ const unreached = unreachedCode(cause);
93
+ if (unreached)
94
+ return new TeamMemberAddFailure(`${FAILED}: could not reach the server (${unreached}).`, false);
95
+ const message = cause instanceof Error ? cause.message : String(cause);
96
+ if (!sent || message.startsWith('ORIZU_AUTH_CONTEXT_CHANGED:'))
97
+ return new TeamMemberAddFailure(message, false);
98
+ // Also reached when a sign-in refresh fails after a 401: still told to
99
+ // check, so say only that the invite did not finish.
100
+ return new TeamMemberAddFailure(`Member add did not finish: ${message}`, true);
101
+ },
102
+ });
103
+ });
104
+ }
105
+ /** `orizu teams members add`: the invited member, or an error saying whether the invite may be saved. */
106
+ export async function addTeamMemberWithDeadline(teamSlug, email, request) {
107
+ const exit = await Effect.runPromiseExit(memberAdd(teamSlug, email, request, datasetTransferTimeoutMs()).pipe(Effect.mapError(failure => new Error(failure.mayBeSaved ? `${failure.message}\n${mayBeInvited(teamSlug, email)}` : failure.message))));
108
+ if (Exit.isSuccess(exit))
109
+ return exit.value;
110
+ throw Cause.squash(exit.cause);
111
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orizu",
3
- "version": "0.5.44",
3
+ "version": "0.5.46",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -634,20 +634,13 @@ async function addTeamMember() {
634
634
  throw new Error('Usage: orizu teams members add --email <email> [--team <teamSlug>]')
635
635
  }
636
636
 
637
- const response = await authedFetch(`/api/cli/teams/${encodeURIComponent(teamSlug)}/members`, {
638
- method: 'POST',
639
- headers: { 'Content-Type': 'application/json' },
640
- body: JSON.stringify({ email }),
641
- })
642
- if (!response.ok) {
643
- throw new Error(`Failed to add team member: ${await response.text()}`)
644
- }
645
- const data = await parseJsonResponse<{ member: TeamMember }>(response, 'Team member add')
637
+ const { addTeamMemberWithDeadline } = await import('./team-member-add-workflow.js')
638
+ const member = await addTeamMemberWithDeadline(teamSlug, email, authedFetch)
646
639
  if (hasJsonFlag()) {
647
- printJson({ member: data.member })
640
+ printJson({ member })
648
641
  return
649
642
  }
650
- printLine(`Added team member ${sanitizeTerminalText(data.member.email)} (${sanitizeTerminalText(data.member.id)})`)
643
+ printLine(`Added team member ${sanitizeTerminalText(member.email)} (${sanitizeTerminalText(member.id)})`)
651
644
  }
652
645
 
653
646
  async function removeTeamMember() {
@@ -60,6 +60,7 @@ import {
60
60
  import { DEFAULT_EGRESS_CANARY_HOST, DEFAULT_HOSTED_MODEL } from './hosted-loop-lifecycle.js'
61
61
  import { composeHostedAnswerPrompt } from './hosted-question.js'
62
62
  import { resumeRunEventSink } from './hosted-run-event-sink.js'
63
+ import { seedProjectFolder } from './hosted-project-folder-seed.js'
63
64
  import { stageOrizuSkill } from './hosted-skill-staging.js'
64
65
  import { openHostedLiveChannel, type HostedLiveChannel } from './hosted-live-channel.js'
65
66
 
@@ -441,6 +442,9 @@ export interface ResolvedSession {
441
442
  agentSessionId: string | null
442
443
  seedOrigin: 'team' | 'session'
443
444
  projectId: string | null
445
+ /** The session's own project's slug and name (ORI-2609), when the server sends them. */
446
+ projectSlug: string | null
447
+ projectName: string | null
444
448
  /** Session lifetime in minutes (from client_info), if the coordinator recorded
445
449
  * it — used to derive the per-prompt max-duration cap (ALI-1061). */
446
450
  durationMinutes: number | null
@@ -547,6 +551,8 @@ export async function resolveSession(opts: {
547
551
  // A server with copying switched off omits seedOrigin; no source is bound.
548
552
  seedOrigin: session.seedOrigin === 'session' ? 'session' : 'team',
549
553
  projectId: asString(session.projectId),
554
+ projectSlug: asString(session.projectSlug),
555
+ projectName: asString(session.projectName),
550
556
  }
551
557
  }
552
558
 
@@ -1342,6 +1348,18 @@ const c = JSON.parse(fs.readFileSync(process.argv[2], 'utf8'));
1342
1348
  exec('git', ['-C', workspaceDir, 'config', 'user.email', AGENT_GIT_IDENTITY.email])
1343
1349
  if (!wokenDisk && !sessionCopyFilled) log(`cloned ${session.repoBranch}`)
1344
1350
 
1351
+ // 5a — ORI-2609: the team copy holds only the projects the team seed knew
1352
+ // about. Write this session's project folder if it is missing, before the
1353
+ // agent starts. Never fails the boot. A woken disk is the agent's own saved
1354
+ // state, so a folder it removed there is not put back.
1355
+ log(wokenDisk
1356
+ ? 'project folder left as the woken disk has it'
1357
+ : seedProjectFolder({
1358
+ workTree: workspaceDir,
1359
+ project: { id: session.projectId, slug: session.projectSlug, name: session.projectName },
1360
+ exec: (cmd, args) => exec(cmd, args),
1361
+ }))
1362
+
1345
1363
  // 5b — Stage the orizu skill into the cloned repo so the agent discovers the
1346
1364
  // Orizu workflows (ALI-1059). SHARED with the operator path via `stageOrizuSkill`
1347
1365
  // (one resolution chain + the harvest-safe .git/info/exclude append). Non-fatal:
@@ -0,0 +1,95 @@
1
+ /**
2
+ * ORI-2609: every project of an approved team shares the team's repository,
3
+ * but the team seed wrote only the first project's folder. A hosted session
4
+ * for another project writes that project's folder into its own checkout at
5
+ * boot, exactly as the team seed would have, and commits it locally. The
6
+ * session's normal save carries the commit; nothing is pushed here.
7
+ *
8
+ * A folder that is already there, whole or partial, is never touched, so a
9
+ * second session never duplicates or overwrites one. Never throws: a seed
10
+ * failure is reported for the boot to log, and the session still starts.
11
+ */
12
+ import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs'
13
+ import { dirname, join } from 'node:path'
14
+
15
+ import {
16
+ buildSeedContractFiles,
17
+ SEED_COMMIT_AUTHOR_EMAIL,
18
+ SEED_COMMIT_AUTHOR_NAME,
19
+ SEED_COMMIT_DATE,
20
+ } from './workspace-bootstrap-seed.js'
21
+
22
+ export interface ProjectFolderSeedProject {
23
+ id: string | null
24
+ slug: string | null
25
+ name: string | null
26
+ }
27
+
28
+ type SeedExec = (cmd: string, args: string[]) => { status: number; stdout: string; stderr: string }
29
+
30
+ // One plain folder name: no separators, no traversal, no leading dot or dash.
31
+ const SAFE_PROJECT_SLUG = /^[A-Za-z0-9][A-Za-z0-9._-]*$/
32
+
33
+ function readTeamFacts(workTree: string): { id: string; slug: string } {
34
+ // The team manifest the team seed wrote is the source of the team facts, so
35
+ // the new project's manifest agrees with the repository it lands in.
36
+ const manifest = JSON.parse(readFileSync(join(workTree, 'orizu.team.json'), 'utf8')) as Record<string, unknown>
37
+ if (typeof manifest.slug !== 'string' || manifest.slug.length === 0) {
38
+ throw new Error('orizu.team.json carries no team slug')
39
+ }
40
+ // The team id does not appear in project files; it is passed for completeness.
41
+ return { id: typeof manifest.teamId === 'string' ? manifest.teamId : '', slug: manifest.slug }
42
+ }
43
+
44
+ /** Returns the one line the boot logs. */
45
+ export function seedProjectFolder(input: {
46
+ workTree: string
47
+ project: ProjectFolderSeedProject
48
+ exec: SeedExec
49
+ }): string {
50
+ const { workTree, project, exec } = input
51
+ if (!project.id) return 'project folder skipped: the session names no project'
52
+ // Without the slug there is no folder name; the server leaves it out when it
53
+ // could not read the project.
54
+ if (!project.slug) return 'project folder skipped: the server sent no project slug'
55
+ try {
56
+ const slug = project.slug
57
+ if (!SAFE_PROJECT_SLUG.test(slug)) throw new Error('the project slug is not a plain folder name')
58
+ const folder = `projects/${slug}`
59
+ const projectsDir = join(workTree, 'projects')
60
+ if (existsSync(projectsDir) && !lstatSync(projectsDir).isDirectory()) {
61
+ throw new Error('projects/ is not a folder')
62
+ }
63
+ if (existsSync(join(workTree, folder))) return `project folder ${folder}/ already present`
64
+
65
+ // Byte-for-byte the files the team seed writes for this project.
66
+ const files = buildSeedContractFiles({
67
+ team: readTeamFacts(workTree),
68
+ projects: [{ id: project.id, slug, name: project.name }],
69
+ }).filter(file => file.path.startsWith(`${folder}/`))
70
+ for (const file of files) {
71
+ const target = join(workTree, file.path)
72
+ mkdirSync(dirname(target), { recursive: true })
73
+ writeFileSync(target, file.bytes, { mode: 0o644 })
74
+ }
75
+ // No hook the checkout's config names runs inside the boot (as in harvest).
76
+ // The exec seam takes no `env` option, so `env` pins the commit dates.
77
+ const git = (step: string, ...args: string[]) => {
78
+ const result = exec('env', [
79
+ `GIT_AUTHOR_DATE=${SEED_COMMIT_DATE}`, `GIT_COMMITTER_DATE=${SEED_COMMIT_DATE}`,
80
+ 'git', '-C', workTree, '-c', 'core.hooksPath=/dev/null', '-c', 'core.fsmonitor=false', ...args,
81
+ ])
82
+ if (result.status !== 0) throw new Error(`git ${step} failed: ${(result.stderr || result.stdout).trim()}`)
83
+ }
84
+ git('add', 'add', '--', folder)
85
+ // Paths after `--` commit only this folder, whatever else is staged. A fixed
86
+ // author, committer and date make every session that starts from the same
87
+ // team head produce the same commit, so two sessions' publishes share it
88
+ // rather than conflict on files neither of them touched.
89
+ git('commit', '-c', 'commit.gpgsign=false', '-c', `user.name=${SEED_COMMIT_AUTHOR_NAME}`, '-c', `user.email=${SEED_COMMIT_AUTHOR_EMAIL}`,
90
+ 'commit', '--quiet', '--no-verify', '-m', `Add the ${slug} project folder`, '--', folder)
91
+ return `project folder ${folder}/ seeded`
92
+ } catch (error) {
93
+ return `project folder seed failed: ${error instanceof Error ? error.message : String(error)}`
94
+ }
95
+ }
@@ -76,6 +76,36 @@ function redactString(input: string, secrets: readonly string[]): string {
76
76
  return out
77
77
  }
78
78
 
79
+ /**
80
+ * The shape pass alone, for servers that hold no secret values: the web app
81
+ * and the session coordinator scrub agent text with it before it reaches a
82
+ * browser (ORI-1905). One list of shapes for the CLI and both servers.
83
+ */
84
+ export function redactSecretShapes(input: string): string {
85
+ return redactString(input, [])
86
+ }
87
+
88
+ // A known credential prefix at the end of the text, followed by fewer
89
+ // characters than its shape above needs: the start of a token still being
90
+ // written. Each run is bounded, so the scan stays linear in the text, and a
91
+ // longer run that is not a token (a path, a word) is left alone. `Bearer` only
92
+ // counts in an Authorization header, so prose like "a Bearer token" keeps its
93
+ // last word.
94
+ const OPEN_TOKEN_AT_END = /(?:\bgithub_pat_[A-Za-z0-9_]{0,19}|\bgh[posur]_[A-Za-z0-9_]{0,19}|\borizu_(?:agent|pat)_[A-Za-z0-9_-]{0,9}|\bsk-[A-Za-z0-9_-]{0,19}|\b(authorization:\s*bearer)\s+[A-Za-z0-9._~+/-]{0,15})$/i
95
+
96
+ /**
97
+ * For reply text shown as it is written: each snapshot is the reply so far, so
98
+ * one can end part-way through a token, before it is long enough for its shape
99
+ * to match. Such a tail is redacted too. The saved read shows a reply's last
100
+ * snapshot, so a finished reply that really ends in a bare `sk-` loses those
101
+ * characters there as well.
102
+ */
103
+ export function redactStreamedTextShapes(input: string): string {
104
+ return redactSecretShapes(input).replace(OPEN_TOKEN_AT_END, (_match, header?: string) =>
105
+ header ? `${header} ${REDACTION_PLACEHOLDER}` : REDACTION_PLACEHOLDER
106
+ )
107
+ }
108
+
79
109
  export interface RedactOptions {
80
110
  /** Verbatim secret values to strip wherever they appear (bearer, tokens). */
81
111
  secrets?: readonly string[]
@@ -49,39 +49,64 @@ function materializeConfigPayload(engine: GepaEngine, environment: NodeJS.Proces
49
49
  return () => rmSync(root, { recursive: true, force: true })
50
50
  }
51
51
 
52
+ const STOP_SIGNALS = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'] as const
53
+ // The optimizer marks its run stopped before exiting (at most 10 s for the
54
+ // status write); past this it is killed so nothing is left running (ORI-2402).
55
+ const CHILD_STOP_WAIT_MS = 15_000
56
+
57
+ /**
58
+ * Run the optimizer, passing a stop signal on to it. On a stop signal the CLI
59
+ * waits until the optimizer exits (capped at CHILD_STOP_WAIT_MS, then kills
60
+ * it) and then ends by that same signal.
61
+ */
52
62
  export async function spawnSkilledProposerChild(
53
63
  command: string, args: string[], engine: GepaEngine, environment: NodeJS.ProcessEnv,
54
64
  onSpawn?: () => Promise<void>,
55
65
  output?: SkilledProposerChildOutput,
56
66
  ) {
57
67
  const childEnvironment = { ...environment }
58
- const shouldMaterialize = engine === 'official'
59
- && childEnvironment.ORIZU_CANDIDATE_PROPOSER === 'skilled-proposer'
60
- && childEnvironment.ORIZU_SKILLED_PROPOSER_CONFIG !== undefined
61
- if (!shouldMaterialize) return spawnSync(command, args, { stdio: 'inherit', env: childEnvironment })
62
-
63
68
  let child: ReturnType<typeof spawn> | PipedChild | undefined
64
69
  let cleanup = () => {}
65
- const signals = ['SIGINT', 'SIGTERM', 'SIGHUP', 'SIGQUIT'] as const
66
- const handlers = signals.map(signal => ({
70
+ let stopSignal: NodeJS.Signals | undefined
71
+ // On the terminal path the optimizer leads its own process group, so a stop
72
+ // also reaches its runners, which may run in worker threads it can't stop.
73
+ let childGroup: number | undefined
74
+ const forward = (signal: NodeJS.Signals) => {
75
+ if (childGroup) {
76
+ try { process.kill(-childGroup, signal); return } catch { /* group already gone */ }
77
+ }
78
+ try { child?.kill(signal) } catch { /* already exited */ }
79
+ }
80
+ let giveUp: ReturnType<typeof setTimeout> | undefined
81
+ const removeHandlers = () => { for (const entry of handlers) process.off(entry.signal, entry.handle) }
82
+ const endBySignal = (signal: NodeJS.Signals): Promise<never> => {
83
+ if (giveUp) clearTimeout(giveUp)
84
+ removeHandlers()
85
+ cleanup()
86
+ // With our handlers gone, the signal's default action ends the process.
87
+ process.kill(process.pid, signal)
88
+ return new Promise<never>(() => {})
89
+ }
90
+ const handlers = STOP_SIGNALS.map(signal => ({
67
91
  signal,
68
92
  handle: () => {
69
- try { cleanup() }
70
- finally {
71
- try { child?.kill(signal) }
72
- finally {
73
- for (const entry of handlers) process.off(entry.signal, entry.handle)
74
- process.kill(process.pid, signal)
75
- }
76
- }
93
+ if (!child) { void endBySignal(signal); return }
94
+ forward(signal)
95
+ if (stopSignal) return
96
+ stopSignal = signal
97
+ // The child read the private payload at start and a stopping run
98
+ // proposes nothing, so it goes now rather than when the child exits.
99
+ cleanup()
100
+ giveUp = setTimeout(() => forward('SIGKILL'), CHILD_STOP_WAIT_MS)
77
101
  },
78
102
  }))
79
- for (const entry of handlers) process.once(entry.signal, entry.handle)
103
+ for (const entry of handlers) process.on(entry.signal, entry.handle)
80
104
  try {
81
105
  cleanup = materializeConfigPayload(engine, childEnvironment) ?? cleanup
82
106
  await new Promise<void>(resolve => setImmediate(resolve))
107
+ let result: { status: number | null, error?: Error }
83
108
  if (output) {
84
- return await runPipedChild(
109
+ result = await runPipedChild(
85
110
  command,
86
111
  args,
87
112
  childEnvironment,
@@ -89,27 +114,44 @@ export async function spawnSkilledProposerChild(
89
114
  output,
90
115
  spawnedChild => { child = spawnedChild }
91
116
  )
117
+ } else {
118
+ // stdin is not passed on: a process outside the terminal's foreground
119
+ // group that read the terminal would be stopped by it.
120
+ // Outside the CLI's group, the optimizer would outlive a SIGKILL of the
121
+ // CLI; it watches for that and stops itself.
122
+ const spawnedChild = spawn(command, args, {
123
+ stdio: ['ignore', 'inherit', 'inherit'],
124
+ env: { ...childEnvironment, ORIZU_STOP_WITH_PARENT: '1' },
125
+ detached: true,
126
+ })
127
+ child = spawnedChild
128
+ childGroup = spawnedChild.pid
129
+ const spawned = new Promise<void>((resolve, reject) => {
130
+ spawnedChild.once('spawn', resolve)
131
+ spawnedChild.once('error', reject)
132
+ })
133
+ const closed = new Promise<{ status: number | null, error?: Error }>(resolve => {
134
+ let error: Error | undefined
135
+ spawnedChild.once('error', cause => { error = cause })
136
+ spawnedChild.once('close', status => resolve(error ? { status, error } : { status }))
137
+ })
138
+ // A launch failure (e.g. ENOENT) is returned, not thrown, so callers
139
+ // name the engine as they did with spawnSync.
140
+ try { await spawned }
141
+ catch (error) { return { status: null, error: error as Error } }
142
+ try { await onSpawn?.() }
143
+ catch (error) { spawnedChild.kill('SIGTERM'); await closed; throw error }
144
+ result = await closed
145
+ // A runner that ignored the stop must not outlive the optimizer.
146
+ if (stopSignal) {
147
+ try { process.kill(-spawnedChild.pid!, 'SIGKILL') } catch { /* group already gone */ }
148
+ }
92
149
  }
93
- const spawnedChild = spawn(command, args, {
94
- stdio: 'inherit',
95
- env: childEnvironment,
96
- })
97
- child = spawnedChild
98
- const spawned = new Promise<void>((resolve, reject) => {
99
- spawnedChild.once('spawn', resolve)
100
- spawnedChild.once('error', reject)
101
- })
102
- const closed = new Promise<{ status: number | null, error?: Error }>(resolve => {
103
- let error: Error | undefined
104
- spawnedChild.once('error', cause => { error = cause })
105
- spawnedChild.once('close', status => resolve(error ? { status, error } : { status }))
106
- })
107
- await spawned
108
- try { await onSpawn?.() }
109
- catch (error) { spawnedChild.kill('SIGTERM'); await closed; throw error }
110
- return await closed
150
+ if (stopSignal) return await endBySignal(stopSignal)
151
+ return result
111
152
  } finally {
112
- for (const entry of handlers) process.off(entry.signal, entry.handle)
153
+ if (giveUp) clearTimeout(giveUp)
154
+ removeHandlers()
113
155
  cleanup()
114
156
  }
115
157
  }
@@ -0,0 +1,113 @@
1
+ import { Cause, Effect, Exit, Result, Schema } from 'effect'
2
+
3
+ import { datasetTransferTimeoutMs } from './dataset-transfer-policy.js'
4
+ import { extractErrorMessage } from './error-response.js'
5
+ import { sanitizeTerminalText } from './json-response.js'
6
+ import { appErrorOf, unreachedCode } from './request-outcome.js'
7
+
8
+ // Loaded only through `await import(...)`, like the dataset workflows.
9
+
10
+ const FAILED = 'Failed to add team member'
11
+ // What the route says when it cannot tell whether the invite was saved (ORI-2383 slice A).
12
+ const SERVER_MAY_BE_SAVED = 'Invite may already be saved. Refresh the member list before inviting again.'
13
+
14
+ // The route saves the address trimmed and lowercased, so the check names that address.
15
+ const mayBeInvited = (teamSlug: string, email: string) => {
16
+ const saved = sanitizeTerminalText(email.trim().toLowerCase())
17
+ return `${saved} may already be invited: run \`orizu teams members list --team ${teamSlug}\` and look for ${saved}. If they are listed, they were invited; don't invite them again.`
18
+ }
19
+
20
+ const AddReply = Schema.Struct({
21
+ member: Schema.Struct({
22
+ id: Schema.NonEmptyString,
23
+ email: Schema.String,
24
+ }),
25
+ })
26
+
27
+ /** The member as the server sent it; `--json` prints every field it has. */
28
+ export type AddedMember = typeof AddReply.Type['member'] & Readonly<Record<string, unknown>>
29
+
30
+ /** Sends one request; calls `onRequestStart` just before anything goes out. */
31
+ export type TeamMemberAddRequest = (path: string, init: RequestInit, onRequestStart?: () => void) => Promise<Response>
32
+
33
+ // The invite may be saved when the server says so, when the reply was lost,
34
+ // late or unreadable after the request was sent, when a 5xx did not come from
35
+ // the app (a gateway giving up while the app kept working), or when the reply
36
+ // has no member. Any other error reply is a clear refusal.
37
+ class TeamMemberAddFailure extends Error {
38
+ readonly _tag = 'TeamMemberAddFailure'
39
+ constructor(message: string, readonly mayBeSaved: boolean) { super(message) }
40
+ }
41
+
42
+ // One request, never retried: a retry after a saved invite is refused as
43
+ // "already a member", which hides that the first one worked.
44
+ function memberAdd(teamSlug: string, email: string, request: TeamMemberAddRequest, timeoutMs: number): Effect.Effect<AddedMember, TeamMemberAddFailure> {
45
+ return Effect.suspend(() => {
46
+ let sent = false
47
+ let timedOut = false
48
+ return Effect.tryPromise({
49
+ try: async signal => {
50
+ const controller = new AbortController()
51
+ const abort = () => controller.abort()
52
+ const timer = setTimeout(() => { timedOut = true; controller.abort() }, timeoutMs)
53
+ if (signal.aborted) abort()
54
+ else signal.addEventListener('abort', abort, { once: true })
55
+ try {
56
+ const response = await request(`/api/cli/teams/${encodeURIComponent(teamSlug)}/members`, {
57
+ method: 'POST',
58
+ headers: { 'Content-Type': 'application/json' },
59
+ body: JSON.stringify({ email }),
60
+ signal: controller.signal,
61
+ }, () => { sent = true })
62
+ if (!response.ok) {
63
+ const appError = await appErrorOf(response.clone())
64
+ const detail = await extractErrorMessage(response)
65
+ if (appError === SERVER_MAY_BE_SAVED) throw new TeamMemberAddFailure(appError, true)
66
+ if (response.status < 400 || (response.status >= 500 && appError === null)) {
67
+ throw new TeamMemberAddFailure(`Member add got no clear answer (status ${response.status}).`, true)
68
+ }
69
+ // The route's own failures already say what failed.
70
+ throw new TeamMemberAddFailure(detail.startsWith('Failed to ') ? detail : `${FAILED}: ${detail}`, false)
71
+ }
72
+ // Reading the body can still fail or stall; that stays under the deadline.
73
+ const body = await response.text()
74
+ let data: unknown
75
+ try {
76
+ data = JSON.parse(body)
77
+ } catch {
78
+ data = undefined
79
+ }
80
+ if (Result.isFailure(Schema.decodeUnknownResult(AddReply)(data))) {
81
+ throw new TeamMemberAddFailure(`Member add returned an unexpected response (status ${response.status}).`, true)
82
+ }
83
+ // Decoding keeps only the named fields; the reply's member is returned whole.
84
+ return (data as { member: AddedMember }).member
85
+ } finally {
86
+ clearTimeout(timer)
87
+ signal.removeEventListener('abort', abort)
88
+ controller.abort()
89
+ }
90
+ },
91
+ catch: cause => {
92
+ if (cause instanceof TeamMemberAddFailure) return cause
93
+ if (timedOut) return new TeamMemberAddFailure('Member add exceeded its deadline.', sent)
94
+ const unreached = unreachedCode(cause)
95
+ if (unreached) return new TeamMemberAddFailure(`${FAILED}: could not reach the server (${unreached}).`, false)
96
+ const message = cause instanceof Error ? cause.message : String(cause)
97
+ if (!sent || message.startsWith('ORIZU_AUTH_CONTEXT_CHANGED:')) return new TeamMemberAddFailure(message, false)
98
+ // Also reached when a sign-in refresh fails after a 401: still told to
99
+ // check, so say only that the invite did not finish.
100
+ return new TeamMemberAddFailure(`Member add did not finish: ${message}`, true)
101
+ },
102
+ })
103
+ })
104
+ }
105
+
106
+ /** `orizu teams members add`: the invited member, or an error saying whether the invite may be saved. */
107
+ export async function addTeamMemberWithDeadline(teamSlug: string, email: string, request: TeamMemberAddRequest): Promise<AddedMember> {
108
+ const exit = await Effect.runPromiseExit(memberAdd(teamSlug, email, request, datasetTransferTimeoutMs()).pipe(
109
+ Effect.mapError(failure => new Error(failure.mayBeSaved ? `${failure.message}\n${mayBeInvited(teamSlug, email)}` : failure.message)),
110
+ ))
111
+ if (Exit.isSuccess(exit)) return exit.value
112
+ throw Cause.squash(exit.cause)
113
+ }
@@ -4,6 +4,11 @@ from __future__ import annotations
4
4
 
5
5
  import json
6
6
  import os
7
+ import signal
8
+ import sys
9
+ import threading
10
+ import time
11
+ from contextlib import contextmanager
7
12
  from pathlib import Path
8
13
  from typing import Any
9
14
  from uuid import uuid4
@@ -431,7 +436,120 @@ def _result_summary(*, run_id: str, best_id: str, best_score: float, seed_score:
431
436
  }
432
437
 
433
438
 
439
+ _STOP_SIGNALS = (signal.SIGINT, signal.SIGTERM, signal.SIGHUP, signal.SIGQUIT)
440
+ # One Ctrl-C reaches the optimizer twice: from the terminal and again from the
441
+ # CLI that passes it on. A repeat this soon after the first is the same press.
442
+ _REPEAT_WINDOW_SECONDS = 1.0
443
+ _STATUS_WRITE_SECONDS = 10.0
444
+
445
+
446
+ class RunInterrupted(BaseException):
447
+ """A stop signal (Ctrl-C, SIGTERM, a closed terminal) reached the run."""
448
+
449
+ def __init__(self, signal_name: str, signum: int) -> None:
450
+ super().__init__(signal_name)
451
+ self.signal_name = signal_name
452
+ self.signum = signum
453
+
454
+
455
+ def _end_by_signal(signum: int) -> None:
456
+ """Exit the way an unhandled signal would, so callers see 128 + its number."""
457
+ sys.stdout.flush()
458
+ sys.stderr.flush()
459
+ signal.signal(signum, signal.SIG_DFL)
460
+ os.kill(os.getpid(), signum)
461
+
462
+
463
+ @contextmanager
464
+ def interrupt_signals():
465
+ """Turn the first stop signal into ``RunInterrupted``; a later one ends at once."""
466
+ first_at: list[float] = []
467
+
468
+ def handle(signum: int, _frame: Any) -> None:
469
+ if not first_at:
470
+ first_at.append(time.monotonic())
471
+ if os.getpgrp() == os.getpid():
472
+ # Runners may run in worker threads, which this interrupt does
473
+ # not reach; when we lead the process group, stop them too.
474
+ # Our own copy of the signal falls in the repeat window.
475
+ try:
476
+ os.killpg(os.getpgrp(), signum)
477
+ except OSError:
478
+ pass
479
+ raise RunInterrupted(signal.Signals(signum).name, signum)
480
+ if time.monotonic() - first_at[0] < _REPEAT_WINDOW_SECONDS:
481
+ return
482
+ _end_by_signal(signum)
483
+
484
+ previous = {signum: signal.getsignal(signum) for signum in _STOP_SIGNALS}
485
+ for signum in _STOP_SIGNALS:
486
+ signal.signal(signum, handle)
487
+ try:
488
+ yield
489
+ finally:
490
+ for signum, handler in previous.items():
491
+ signal.signal(signum, handler)
492
+
493
+
494
+ def _stop_when_parent_exits() -> None:
495
+ """Stop like SIGTERM once the CLI that started us is gone (e.g. SIGKILLed).
496
+
497
+ The CLI puts the optimizer in its own process group, so a signal sent to
498
+ the CLI's group no longer reaches it; without this it would keep running.
499
+ """
500
+ parent = os.getppid()
501
+
502
+ def watch() -> None:
503
+ while os.getppid() == parent:
504
+ time.sleep(0.5)
505
+ os.kill(os.getpid(), signal.SIGTERM)
506
+
507
+ threading.Thread(target=watch, daemon=True).start()
508
+
509
+
510
+ def _mark_interrupted_run(client: OrizuClient, run_id: str, signal_name: str) -> None:
511
+ """Mark a stopped local run cancelled once, waiting at most 10 s; never retry."""
512
+ outcome: list[BaseException | None] = []
513
+
514
+ def write() -> None:
515
+ try:
516
+ client.update_run(run_id, status="cancelled",
517
+ metadata={"stop_reason": "interrupted", "signal": signal_name})
518
+ outcome.append(None)
519
+ except BaseException as error: # reported below; the run still stops
520
+ outcome.append(error)
521
+
522
+ writer = threading.Thread(target=write, daemon=True)
523
+ writer.start()
524
+ writer.join(_STATUS_WRITE_SECONDS)
525
+ if not outcome or outcome[0] is not None:
526
+ print(
527
+ f"Run {run_id} may still show as running: run "
528
+ f"`orizu optimizations cancel {run_id}` to mark it stopped.",
529
+ file=sys.stderr, flush=True,
530
+ )
531
+
532
+
434
533
  def run_from_environment() -> dict[str, Any]:
534
+ """Run official GEPA; a stopped local run is marked cancelled (ORI-2402).
535
+
536
+ A hosted run's final status belongs to its coordinator, which records it
537
+ before stopping the sandbox, so a hosted run writes no status here.
538
+ """
539
+ started: dict[str, Any] = {}
540
+ with interrupt_signals():
541
+ if os.environ.get("ORIZU_STOP_WITH_PARENT") == "1":
542
+ _stop_when_parent_exits()
543
+ try:
544
+ return _run_from_environment(started)
545
+ except RunInterrupted as interrupt:
546
+ if started.get("run_id") and not started.get("hosted"):
547
+ _mark_interrupted_run(started["client"], started["run_id"], interrupt.signal_name)
548
+ _end_by_signal(interrupt.signum)
549
+ raise
550
+
551
+
552
+ def _run_from_environment(started: dict[str, Any]) -> dict[str, Any]:
435
553
  """Run official GEPA over the verified legacy runner subprocess contract.
436
554
 
437
555
  ALI-1502 supplies these variables after verifying runner bytes. Scorer
@@ -542,6 +660,7 @@ def run_from_environment() -> dict[str, Any]:
542
660
  model_config_settings_version_id=instruction_set.get("model_config_settings_version_id") if isinstance(instruction_set, dict) else None,
543
661
  runner_version_id=prompt_context.runner_version_id,
544
662
  )
663
+ started.update(client=client, run_id=run_id, hosted=hosted_run_id is not None)
545
664
  print(json.dumps({"optimization_run_id": run_id}), flush=True)
546
665
  # The frozen sink treats ``None`` as no local artifact writer. Keep that
547
666
  # legacy behavior rather than maintaining a connector-local no-op logger.
@@ -728,6 +847,9 @@ def run_from_environment() -> dict[str, Any]:
728
847
  total_tokens_out=int(getattr(reflection_lm, "total_tokens_out", 0)),
729
848
  total_tokens=int(getattr(reflection_lm, "total_tokens", 0)),
730
849
  )
850
+ # From here the run has its own final status; a stop must not
851
+ # overwrite it with "cancelled".
852
+ started.pop("run_id", None)
731
853
  if budget_exhausted:
732
854
  if hosted_run_id is None:
733
855
  client.update_run(
@@ -764,6 +886,7 @@ def run_from_environment() -> dict[str, Any]:
764
886
  temporary_result_path.replace(result_path)
765
887
  return summary
766
888
  except Exception as error:
889
+ started.pop("run_id", None)
767
890
  if sink.failed:
768
891
  sink.retry_failed_status()
769
892
  elif hosted_run_id is None: