negotium 0.23.2 → 0.23.4

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 (30) hide show
  1. package/dist/agent-helpers.js +171 -12
  2. package/dist/agent-helpers.js.map +8 -7
  3. package/dist/{chunk-s34sv3d2.js → chunk-ggdwn8d5.js} +3 -3
  4. package/dist/{chunk-s34sv3d2.js.map → chunk-ggdwn8d5.js.map} +2 -2
  5. package/dist/{chunk-9zn0bkc9.js → chunk-jarf6nb2.js} +2 -2
  6. package/dist/{chunk-9zn0bkc9.js.map → chunk-jarf6nb2.js.map} +1 -1
  7. package/dist/{chunk-cez5m5hm.js → chunk-pwafy9bb.js} +1 -1
  8. package/dist/{chunk-q5djsg78.js → chunk-qekt5bnd.js} +1 -1
  9. package/dist/{chunk-6cnpkzkb.js → chunk-vtd5kn4n.js} +1 -1
  10. package/dist/{chunk-6cnpkzkb.js.map → chunk-vtd5kn4n.js.map} +3 -3
  11. package/dist/hosted-agent.js +166 -13
  12. package/dist/hosted-agent.js.map +8 -7
  13. package/dist/main.js +240 -21
  14. package/dist/main.js.map +11 -10
  15. package/dist/mcp-factories.js +171 -12
  16. package/dist/mcp-factories.js.map +8 -7
  17. package/dist/registry.js +3 -3
  18. package/dist/rollout.js +2 -2
  19. package/dist/runtime/src/agents/claude-notices.ts +130 -0
  20. package/dist/runtime/src/agents/claude-provider.ts +9 -11
  21. package/dist/runtime/src/agents/codex-provider.ts +51 -6
  22. package/dist/runtime/src/agents/rollout/codex.ts +58 -0
  23. package/dist/runtime/src/version.ts +1 -1
  24. package/dist/types/packages/core/src/agents/claude-notices.d.ts +15 -0
  25. package/dist/types/packages/core/src/agents/codex-provider.d.ts +6 -0
  26. package/dist/types/packages/core/src/agents/rollout/codex.d.ts +12 -0
  27. package/dist/types/packages/core/src/version.d.ts +1 -1
  28. package/package.json +1 -1
  29. /package/dist/{chunk-cez5m5hm.js.map → chunk-pwafy9bb.js.map} +0 -0
  30. /package/dist/{chunk-q5djsg78.js.map → chunk-qekt5bnd.js.map} +0 -0
package/dist/registry.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  encodeClaudeCwd,
4
4
  writeClaudeRollout
5
- } from "./chunk-cez5m5hm.js";
5
+ } from "./chunk-pwafy9bb.js";
6
6
  import {
7
7
  CLAUDE_EFFORT_VALUES,
8
8
  CODEX_EFFORT_VALUES,
@@ -17,7 +17,7 @@ import {
17
17
  hostedCodexHomePath,
18
18
  logger,
19
19
  writeCodexRollout
20
- } from "./chunk-6cnpkzkb.js";
20
+ } from "./chunk-vtd5kn4n.js";
21
21
 
22
22
  // ../../packages/core/src/agents/claude-registry.ts
23
23
  import { existsSync, mkdirSync, readdirSync, renameSync, unlinkSync } from "fs";
@@ -132,7 +132,7 @@ var codexRegistryOperations = {
132
132
  return { sessionId: threadId, rolloutPath };
133
133
  },
134
134
  async forkSession({ parentSessionId }) {
135
- const { forkCodexSession } = await import("./chunk-s34sv3d2.js");
135
+ const { forkCodexSession } = await import("./chunk-ggdwn8d5.js");
136
136
  return await forkCodexSession(parentSessionId);
137
137
  },
138
138
  async cleanupRollouts({ sessionIds }) {
package/dist/rollout.js CHANGED
@@ -3,7 +3,7 @@ import {
3
3
  encodeClaudeCwd,
4
4
  repairPoisonedRollout,
5
5
  writeClaudeRollout
6
- } from "./chunk-cez5m5hm.js";
6
+ } from "./chunk-pwafy9bb.js";
7
7
  import {
8
8
  configureRolloutHost,
9
9
  decodeUuidV7Timestamp,
@@ -16,7 +16,7 @@ import {
16
16
  readLatestCodexContextUsage,
17
17
  readLatestCodexPatchPreview,
18
18
  writeCodexRollout
19
- } from "./chunk-6cnpkzkb.js";
19
+ } from "./chunk-vtd5kn4n.js";
20
20
 
21
21
  // src/rollout.ts
22
22
  import { fileURLToPath } from "url";
@@ -0,0 +1,130 @@
1
+ /**
2
+ * Non-fatal Claude Agent SDK notices → user-visible status lines.
3
+ *
4
+ * Fatality is decided only by the terminal `result` message or a CLI exit
5
+ * (see claude-provider.ts); nothing here may end a turn. The SDK adds and
6
+ * renames system subtypes often, so the handled subtypes are keyed by the
7
+ * SDK's own types: a rename or removal fails `tsc` on upgrade, while a
8
+ * subtype this table does not know is only debug-logged at runtime.
9
+ */
10
+
11
+ import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
12
+ import { logger } from "#platform/logger";
13
+
14
+ type SystemMessage = Extract<SDKMessage, { type: "system" }>;
15
+ type SystemSubtype = SystemMessage["subtype"];
16
+ type SystemOf<S extends SystemSubtype> = Extract<SystemMessage, { subtype: S }>;
17
+ type RateLimitEvent = Extract<SDKMessage, { type: "rate_limit_event" }>;
18
+ type ApiError = SystemOf<"api_retry">["error"];
19
+ type RateLimitType = NonNullable<RateLimitEvent["rate_limit_info"]["rateLimitType"]>;
20
+
21
+ // Exhaustive over the SDK unions, so a new reason or limit fails `tsc`.
22
+ const API_ERROR_TEXT: Record<ApiError, string> = {
23
+ overloaded: "Claude is overloaded",
24
+ rate_limit: "Claude rate limit hit",
25
+ server_error: "Claude API error",
26
+ unknown: "Claude request failed",
27
+ authentication_failed: "Claude sign-in failed",
28
+ oauth_org_not_allowed: "Organization not allowed for Claude",
29
+ account_on_hold: "Claude account is on hold",
30
+ verification_required: "Claude account needs verification",
31
+ billing_error: "Claude billing issue",
32
+ invalid_request: "Claude rejected the request",
33
+ model_not_found: "Claude model not found",
34
+ max_output_tokens: "Claude reply hit the length limit",
35
+ cloud_credential_error: "Cloud credentials failed",
36
+ };
37
+
38
+ const RATE_LIMIT_TEXT: Record<RateLimitType, string> = {
39
+ five_hour: "5-hour",
40
+ seven_day: "weekly",
41
+ seven_day_opus: "weekly Opus",
42
+ seven_day_sonnet: "weekly Sonnet",
43
+ seven_day_overage_included: "weekly",
44
+ overage: "extra usage",
45
+ };
46
+
47
+ const DENIAL_TEXT: Record<string, string> = {
48
+ rule: "by a permission rule",
49
+ mode: "by the permission mode",
50
+ classifier: "by the safety check",
51
+ asyncAgent: "for a background agent",
52
+ };
53
+
54
+ /** `claude-opus-5-5` → `Opus 5.5`; other ids pass through unchanged. */
55
+ function modelLabel(id: string): string {
56
+ const match = /^claude-([a-z]+)-(\d+)-(\d+)/.exec(id);
57
+ if (!match) return id;
58
+ const [, family = "", major, minor] = match;
59
+ return `${family.charAt(0).toUpperCase()}${family.slice(1)} ${major}.${minor}`;
60
+ }
61
+
62
+ const SYSTEM_NOTICES: {
63
+ [S in SystemSubtype]?: (message: SystemOf<S>) => string | undefined;
64
+ } = {
65
+ api_retry: (m) => {
66
+ const reason =
67
+ m.error_status === null
68
+ ? "Lost connection to Claude"
69
+ : (API_ERROR_TEXT[m.error] ?? "Claude request failed");
70
+ const delay = Math.round(m.retry_delay_ms / 1000);
71
+ const when = delay > 0 ? `in ${delay}s` : "now";
72
+ return `${reason} · retrying ${when} (${m.attempt}/${m.max_retries})`;
73
+ },
74
+ model_refusal_fallback: (m) =>
75
+ m.direction === "retry"
76
+ ? `${modelLabel(m.original_model)} declined · ${modelLabel(m.fallback_model)} is answering instead`
77
+ : m.content || undefined,
78
+ model_refusal_no_fallback: (m) => `${modelLabel(m.original_model)} declined this request`,
79
+ permission_denied: (m) => {
80
+ const tool = m.tool_name.split("__").at(-1) || m.tool_name;
81
+ const why = m.decision_reason
82
+ ? `: ${m.decision_reason}`
83
+ : ` ${DENIAL_TEXT[m.decision_reason_type ?? ""] ?? ""}`.trimEnd();
84
+ return `${tool} was blocked${why}`;
85
+ },
86
+ informational: (m) =>
87
+ m.prevent_continuation || m.level === "warning" || m.level === "suggestion"
88
+ ? m.content
89
+ : undefined,
90
+ mirror_error: (m) => {
91
+ logger.warn({ error: m.error }, "claudeProvider: transcript mirror batch dropped");
92
+ return undefined;
93
+ },
94
+ };
95
+
96
+ function rateLimitNotice(m: RateLimitEvent): string | undefined {
97
+ const { status, rateLimitType } = m.rate_limit_info;
98
+ const limit = rateLimitType ? `${RATE_LIMIT_TEXT[rateLimitType] ?? ""} `.trimStart() : "";
99
+ if (status === "allowed_warning") return `Nearing your ${limit}Claude usage limit`;
100
+ if (status === "rejected") return `Reached your ${limit}Claude usage limit`;
101
+ return undefined;
102
+ }
103
+
104
+ /**
105
+ * Status text for a non-terminal SDK message, or undefined when it should not
106
+ * be shown. Never throws and never classifies anything as an error.
107
+ */
108
+ export function claudeNoticeStatus(message: SDKMessage): string | undefined {
109
+ try {
110
+ if (message.type === "rate_limit_event") return rateLimitNotice(message);
111
+ if (message.type !== "system") return undefined;
112
+ const handler = SYSTEM_NOTICES[message.subtype] as
113
+ | ((m: SystemMessage) => string | undefined)
114
+ | undefined;
115
+ if (!handler) {
116
+ logger.debug(
117
+ { subtype: message.subtype, msg: message },
118
+ "claudeProvider: system message (unhandled subtype)",
119
+ );
120
+ return undefined;
121
+ }
122
+ const status = handler(message);
123
+ if (status) logger.warn({ subtype: message.subtype, status }, "claudeProvider: notice");
124
+ return status;
125
+ } catch (err) {
126
+ // A field reshaped by an SDK upgrade must not take the turn down with it.
127
+ logger.debug({ err, msg: message }, "claudeProvider: notice mapping failed");
128
+ return undefined;
129
+ }
130
+ }
@@ -19,6 +19,7 @@ import type {
19
19
  ContentBlockParam,
20
20
  ImageBlockParam,
21
21
  } from "@anthropic-ai/sdk/resources/messages/messages.mjs";
22
+ import { claudeNoticeStatus } from "#agents/claude-notices";
22
23
  import { claudeRegistry } from "#agents/claude-registry";
23
24
  import { deepMapStrings } from "#agents/deep-map";
24
25
  import {
@@ -703,18 +704,15 @@ export async function* claudeProvider(opts: AgentQueryOptions): AsyncGenerator<U
703
704
  continue;
704
705
  }
705
706
 
706
- // --- System init ---
707
- if (message.type === "system") {
708
- const m = message as SDKSystemMessage;
709
- if (m.subtype === "init") {
710
- yield { type: "session", sessionId: m.session_id };
707
+ // --- System init + non-fatal notices ---
708
+ // Retries, refusals, auto-denials and usage warnings become status lines;
709
+ // only the terminal `result` (or a CLI exit) can fail the turn.
710
+ if (message.type === "system" || message.type === "rate_limit_event") {
711
+ if (message.type === "system" && message.subtype === "init") {
712
+ yield { type: "session", sessionId: (message as SDKSystemMessage).session_id };
711
713
  } else {
712
- // Log all other system messages (task_updated, task_progress,
713
- // task_notification, etc.) so we can observe lifecycle events.
714
- logger.debug(
715
- { subtype: m.subtype, msg: m },
716
- "claudeProvider: system message (unhandled subtype)",
717
- );
714
+ const status = claudeNoticeStatus(message);
715
+ if (status) yield { type: "status", content: status };
718
716
  }
719
717
  continue;
720
718
  }
@@ -37,6 +37,7 @@ import {
37
37
  } from "#agents/execution-host";
38
38
  import {
39
39
  type CodexTokenTotals,
40
+ codexThreadHasSystemPrompt,
40
41
  migrateCodexRolloutNativeMultiAgentMetadata,
41
42
  readCodexPatchCallIds,
42
43
  readLatestCodexContextUsage,
@@ -64,6 +65,18 @@ type CodexEventUsage = {
64
65
  const CODEX_HOOK_TRUST_BYPASS_NOTICE =
65
66
  "`--dangerously-bypass-hook-trust` is enabled. Enabled hooks may run without review for this invocation.";
66
67
 
68
+ /**
69
+ * One short status line from a free-form Codex notice. Codex sends prose with
70
+ * no stable code, so only the shape is used: its first sentence, labeled as
71
+ * Codex's. The full text stays in the log.
72
+ */
73
+ export function codexNoticeStatus(message: string): string {
74
+ const firstLine = message.trim().split("\n")[0]?.trim() ?? "";
75
+ const sentence = /^.*?[.!?](?=\s|$)/.exec(firstLine)?.[0] ?? firstLine;
76
+ if (!sentence) return "Codex notice";
77
+ return /^codex\b/i.test(sentence) ? sentence : `Codex: ${sentence}`;
78
+ }
79
+
67
80
  function sameCodexUsage(usage: CodexEventUsage, total: CodexTokenTotals): boolean {
68
81
  return (
69
82
  usage.input_tokens === total.inputTokens &&
@@ -760,7 +773,6 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
760
773
  hooks: vaultHook.hooks,
761
774
  model_catalog_json: codexModelCatalogPath,
762
775
  mcp_servers: codexMcpServers,
763
- ...(opts.toolPolicy ? { sandbox_permissions: [] } : {}),
764
776
  },
765
777
  });
766
778
 
@@ -793,13 +805,20 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
793
805
  let currentSessionId = opts.sessionId;
794
806
  let usageBaseline = currentSessionId ? readLatestCodexTokenTotals(currentSessionId) : undefined;
795
807
 
796
- // Codex SDK has no systemPrompt option. A resumed thread may have been
797
- // synthesized from a captured shell, so always refresh the current runtime
798
- // instructions instead of trusting historical developer/environment data.
808
+ // Codex SDK has no systemPrompt option, so the instructions ride in the user
809
+ // prompt and stay in thread history from then on. Re-sending them every turn
810
+ // piles a full copy into history per message. Send them on a new thread, or
811
+ // when the resumed history lacks the current text (synthesized from a
812
+ // captured shell, or the instructions changed since they were last sent).
799
813
  let thread = currentSessionId
800
814
  ? codex.resumeThread(currentSessionId, threadOptions)
801
815
  : codex.startThread(threadOptions);
802
- let prompt = promptForThread(opts, true);
816
+ let prompt = promptForThread(
817
+ opts,
818
+ !currentSessionId ||
819
+ !opts.systemPrompt ||
820
+ !codexThreadHasSystemPrompt(currentSessionId, opts.systemPrompt),
821
+ );
803
822
 
804
823
  let agentTextSoFar = "";
805
824
  let currentAgentMessageId: string | undefined;
@@ -844,6 +863,9 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
844
863
  trackedPids.pids = [];
845
864
  let heartbeatTriggered = false;
846
865
  let attemptCompleted = false;
866
+ let turnFailed = false;
867
+ // Last top-level `error` event; fatal only if the turn never completes.
868
+ let streamError: string | undefined;
847
869
  let heartbeatTimer: ReturnType<typeof setTimeout> | null = null;
848
870
 
849
871
  const scheduleHeartbeat = () => {
@@ -927,6 +949,7 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
927
949
  cache_write_input_tokens?: number;
928
950
  };
929
951
  error?: { message?: string };
952
+ message?: string;
930
953
  };
931
954
 
932
955
  switch (event.type) {
@@ -1035,9 +1058,14 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
1035
1058
  yield fileEvent;
1036
1059
  }
1037
1060
  } else if (item.type === "error") {
1061
+ // The SDK types error items as non-fatal: the turn keeps running
1062
+ // and real failures arrive as `turn.failed` or a top-level
1063
+ // `error`. Yielding an error here would end the turn (and fail
1064
+ // compaction) over a notice such as an ignored config key.
1038
1065
  const message = String(item.message ?? "");
1039
1066
  if (message !== CODEX_HOOK_TRUST_BYPASS_NOTICE) {
1040
- yield { type: "error", content: message };
1067
+ logger.warn({ message }, "codexProvider: codex non-fatal error item");
1068
+ yield { type: "status", content: codexNoticeStatus(message) };
1041
1069
  }
1042
1070
  }
1043
1071
  break;
@@ -1071,7 +1099,19 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
1071
1099
  break;
1072
1100
  }
1073
1101
 
1102
+ case "error": {
1103
+ // `codex exec` keeps running after a top-level error (e.g. a stream
1104
+ // error it retries); only `turn.failed` or a turn that never
1105
+ // completes is fatal. Keep the message to explain the latter.
1106
+ const message = event.message || "Codex stream error";
1107
+ logger.warn({ message }, "codexProvider: codex stream error event");
1108
+ streamError = message;
1109
+ yield { type: "status", content: codexNoticeStatus(message) };
1110
+ break;
1111
+ }
1112
+
1074
1113
  case "turn.failed": {
1114
+ turnFailed = true;
1075
1115
  stopReason = "error";
1076
1116
  yield {
1077
1117
  type: "error",
@@ -1084,6 +1124,11 @@ export async function* codexProvider(opts: AgentQueryOptions): AsyncGenerator<Un
1084
1124
  break;
1085
1125
  }
1086
1126
  }
1127
+
1128
+ if (!attemptCompleted && !turnFailed && streamError) {
1129
+ stopReason = "error";
1130
+ yield { type: "error", content: streamError };
1131
+ }
1087
1132
  } catch (err) {
1088
1133
  if (abortSignal?.aborted) return;
1089
1134
  if (err instanceof Error && err.name === "AbortError") {
@@ -467,6 +467,64 @@ export function extractCodexForkedFromId(jsonl: string): string | undefined {
467
467
  }
468
468
  }
469
469
 
470
+ /**
471
+ * Text of the latest user turn that carried negotium's `[System Instructions]`
472
+ * preamble. Codex has no system-prompt option, so the provider inlines it into
473
+ * the user prompt; this reports what the resumed thread already holds.
474
+ */
475
+ export function extractLatestCodexSystemInstructionsTurn(jsonl: string): string | undefined {
476
+ const lines = jsonl.trimEnd().split("\n");
477
+ for (let index = lines.length - 1; index >= 0; index -= 1) {
478
+ const line = lines[index] ?? "";
479
+ if (!line.includes("[System Instructions]")) continue;
480
+ try {
481
+ const entry = JSON.parse(line) as {
482
+ type?: string;
483
+ payload?: { type?: string; role?: string; content?: Array<{ text?: unknown }> };
484
+ };
485
+ if (entry.type !== "response_item") continue;
486
+ if (entry.payload?.type !== "message" || entry.payload.role !== "user") continue;
487
+ const text = (entry.payload.content ?? [])
488
+ .map((part) => (typeof part.text === "string" ? part.text : ""))
489
+ .join("");
490
+ if (text.startsWith("[System Instructions]\n")) return text;
491
+ } catch {
492
+ // A concurrently appended final line may be incomplete; keep scanning.
493
+ }
494
+ }
495
+ return undefined;
496
+ }
497
+
498
+ /**
499
+ * Whether the thread's history already ends its instruction trail with exactly
500
+ * `systemPrompt`. Synthesized rollouts (agent switch, fork fallback) carry no
501
+ * instructions, and a changed prompt must be re-sent, so both report false.
502
+ */
503
+ export function codexThreadHasSystemPrompt(
504
+ threadId: string,
505
+ systemPrompt: string,
506
+ depth = 0,
507
+ ): boolean {
508
+ const path = latestCodexRolloutPath(threadId);
509
+ if (!path) return false;
510
+ try {
511
+ const jsonl = readFileSync(path, "utf8");
512
+ const latest = extractLatestCodexSystemInstructionsTurn(jsonl);
513
+ if (latest !== undefined) {
514
+ return latest.startsWith(`[System Instructions]\n${systemPrompt}\n\n`);
515
+ }
516
+ if (depth >= 8) return false;
517
+ // A paginated fork may not copy the parent's items into its own rollout.
518
+ const parentId = extractCodexForkedFromId(jsonl);
519
+ return parentId && parentId !== threadId
520
+ ? codexThreadHasSystemPrompt(parentId, systemPrompt, depth + 1)
521
+ : false;
522
+ } catch (error) {
523
+ logger.debug({ error, threadId }, "codex system prompt: rollout read failed");
524
+ return false;
525
+ }
526
+ }
527
+
470
528
  export function readLatestCodexTokenTotals(
471
529
  threadId: string,
472
530
  depth = 0,
@@ -1 +1 @@
1
- export const NEGOTIUM_VERSION = "0.23.2";
1
+ export const NEGOTIUM_VERSION = "0.23.4";
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Non-fatal Claude Agent SDK notices → user-visible status lines.
3
+ *
4
+ * Fatality is decided only by the terminal `result` message or a CLI exit
5
+ * (see claude-provider.ts); nothing here may end a turn. The SDK adds and
6
+ * renames system subtypes often, so the handled subtypes are keyed by the
7
+ * SDK's own types: a rename or removal fails `tsc` on upgrade, while a
8
+ * subtype this table does not know is only debug-logged at runtime.
9
+ */
10
+ import type { SDKMessage } from "@anthropic-ai/claude-agent-sdk";
11
+ /**
12
+ * Status text for a non-terminal SDK message, or undefined when it should not
13
+ * be shown. Never throws and never classifies anything as an error.
14
+ */
15
+ export declare function claudeNoticeStatus(message: SDKMessage): string | undefined;
@@ -6,6 +6,12 @@ type CodexEventUsage = {
6
6
  cached_input_tokens?: number;
7
7
  cache_write_input_tokens?: number;
8
8
  };
9
+ /**
10
+ * One short status line from a free-form Codex notice. Codex sends prose with
11
+ * no stable code, so only the shape is used: its first sentence, labeled as
12
+ * Codex's. The full text stays in the log.
13
+ */
14
+ export declare function codexNoticeStatus(message: string): string;
9
15
  /**
10
16
  * Some Codex CLI builds expose the thread-lifetime counter on turn.completed
11
17
  * despite the SDK declaring turn-local usage. Only subtract a rollout baseline
@@ -76,6 +76,18 @@ export declare function extractLatestCodexTokenTotals(jsonl: string): CodexToken
76
76
  export declare function readLatestCodexContextUsage(threadId: string): CodexContextUsage | undefined;
77
77
  /** `forked_from_id` from a rollout's leading session_meta line, if any. */
78
78
  export declare function extractCodexForkedFromId(jsonl: string): string | undefined;
79
+ /**
80
+ * Text of the latest user turn that carried negotium's `[System Instructions]`
81
+ * preamble. Codex has no system-prompt option, so the provider inlines it into
82
+ * the user prompt; this reports what the resumed thread already holds.
83
+ */
84
+ export declare function extractLatestCodexSystemInstructionsTurn(jsonl: string): string | undefined;
85
+ /**
86
+ * Whether the thread's history already ends its instruction trail with exactly
87
+ * `systemPrompt`. Synthesized rollouts (agent switch, fork fallback) carry no
88
+ * instructions, and a changed prompt must be re-sent, so both report false.
89
+ */
90
+ export declare function codexThreadHasSystemPrompt(threadId: string, systemPrompt: string, depth?: number): boolean;
79
91
  export declare function readLatestCodexTokenTotals(threadId: string, depth?: number): CodexTokenTotals | undefined;
80
92
  /**
81
93
  * Old and metadata-free Codex rollouts resume as native multi-agent v1 even
@@ -1 +1 @@
1
- export declare const NEGOTIUM_VERSION = "0.23.2";
1
+ export declare const NEGOTIUM_VERSION = "0.23.4";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "negotium",
3
- "version": "0.23.2",
3
+ "version": "0.23.4",
4
4
  "type": "module",
5
5
  "description": "Install the Negotium multi-agent runtime and CLI with one package",
6
6
  "license": "Apache-2.0",