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.
- package/dist/agent-helpers.js +171 -12
- package/dist/agent-helpers.js.map +8 -7
- package/dist/{chunk-s34sv3d2.js → chunk-ggdwn8d5.js} +3 -3
- package/dist/{chunk-s34sv3d2.js.map → chunk-ggdwn8d5.js.map} +2 -2
- package/dist/{chunk-9zn0bkc9.js → chunk-jarf6nb2.js} +2 -2
- package/dist/{chunk-9zn0bkc9.js.map → chunk-jarf6nb2.js.map} +1 -1
- package/dist/{chunk-cez5m5hm.js → chunk-pwafy9bb.js} +1 -1
- package/dist/{chunk-q5djsg78.js → chunk-qekt5bnd.js} +1 -1
- package/dist/{chunk-6cnpkzkb.js → chunk-vtd5kn4n.js} +1 -1
- package/dist/{chunk-6cnpkzkb.js.map → chunk-vtd5kn4n.js.map} +3 -3
- package/dist/hosted-agent.js +166 -13
- package/dist/hosted-agent.js.map +8 -7
- package/dist/main.js +240 -21
- package/dist/main.js.map +11 -10
- package/dist/mcp-factories.js +171 -12
- package/dist/mcp-factories.js.map +8 -7
- package/dist/registry.js +3 -3
- package/dist/rollout.js +2 -2
- package/dist/runtime/src/agents/claude-notices.ts +130 -0
- package/dist/runtime/src/agents/claude-provider.ts +9 -11
- package/dist/runtime/src/agents/codex-provider.ts +51 -6
- package/dist/runtime/src/agents/rollout/codex.ts +58 -0
- package/dist/runtime/src/version.ts +1 -1
- package/dist/types/packages/core/src/agents/claude-notices.d.ts +15 -0
- package/dist/types/packages/core/src/agents/codex-provider.d.ts +6 -0
- package/dist/types/packages/core/src/agents/rollout/codex.d.ts +12 -0
- package/dist/types/packages/core/src/version.d.ts +1 -1
- package/package.json +1 -1
- /package/dist/{chunk-cez5m5hm.js.map → chunk-pwafy9bb.js.map} +0 -0
- /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-
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
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
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
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
|
-
|
|
713
|
-
|
|
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
|
|
797
|
-
//
|
|
798
|
-
//
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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.
|
|
1
|
+
export declare const NEGOTIUM_VERSION = "0.23.4";
|
package/package.json
CHANGED
|
File without changes
|
|
File without changes
|