totemlayer 0.1.67 → 0.1.74

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 (42) hide show
  1. package/README.md +11 -13
  2. package/bin/agent-runner.mjs +23 -1
  3. package/bin/automatic-runtime.mjs +23 -12
  4. package/bin/codex-chat-bridge.mjs +72 -38
  5. package/bin/codex-desktop-wake.mjs +47 -20
  6. package/bin/codex-human-chat-bridge.mjs +57 -21
  7. package/bin/codex-mission-bridge.mjs +167 -0
  8. package/bin/connected-agent-service.mjs +12 -2
  9. package/bin/executor-diagnostic.mjs +169 -61
  10. package/bin/installation.mjs +8 -16
  11. package/bin/local-inspector.mjs +6 -0
  12. package/bin/mcp-install.mjs +102 -5
  13. package/bin/mcp.mjs +43 -1
  14. package/bin/mission-worker.mjs +62 -39
  15. package/bin/model-adapter.mjs +30 -1
  16. package/bin/native/darwin-lifetime +0 -0
  17. package/bin/native/darwin-lifetime.json +1 -1
  18. package/bin/network-contract.mjs +125 -0
  19. package/bin/network-runtime.mjs +367 -315
  20. package/bin/observability.mjs +336 -0
  21. package/bin/platform-client.mjs +6 -3
  22. package/bin/plugin-connect.mjs +10 -0
  23. package/bin/provider-diagnostics.mjs +40 -11
  24. package/bin/runtime-installation.mjs +4 -1
  25. package/bin/runtime-lock.mjs +43 -6
  26. package/bin/sdk/index.d.ts +1 -0
  27. package/bin/sdk/index.js +1 -1
  28. package/bin/setup-assets/api-connect.backup.md +129 -0
  29. package/bin/setup-assets/api-connect.md +129 -0
  30. package/bin/setup-assets/api-manifest.json +1 -0
  31. package/bin/setup-assets/manifest.json +1 -0
  32. package/bin/setup-assets/setup.backup.md +178 -0
  33. package/bin/setup-assets/setup.md +178 -0
  34. package/bin/setup-guide.mjs +39 -0
  35. package/bin/setup-receipts.mjs +181 -0
  36. package/bin/strict-codex-adapter.mjs +456 -0
  37. package/bin/strict-installation.mjs +206 -0
  38. package/bin/strict-setup-jobs.mjs +253 -0
  39. package/bin/strict-setup.mjs +385 -0
  40. package/bin/totem.mjs +11 -1
  41. package/package.json +4 -3
  42. package/scripts/prepare.mjs +2 -0
@@ -1,3 +1,4 @@
1
+ import { withAgentObservation, observeGeneration, observationOutcome } from "./observability.mjs";
1
2
  import { startupDiagnostic } from "./startup-diagnostics.mjs";
2
3
  import { DEFAULT_LISTENER_POLL_MS } from "./sdk/listener.js";
3
4
  import { createHash, randomUUID } from "node:crypto";
@@ -119,42 +120,44 @@ export async function modelReply({
119
120
  model ??= provider === "qwen-local" ? "qwen3.5-9b" : undefined;
120
121
  const endpoint = localModelEndpoint(provider, { ollamaUrl: origin, qwenUrl, nimUrl });
121
122
  try {
122
- const response = await fetcher(endpoint, {
123
- method: "POST",
124
- redirect: "error",
125
- signal: AbortSignal.any([signal, timeout]),
126
- headers: { "Content-Type": "application/json" },
127
- body: JSON.stringify({
128
- model,
129
- messages: openai
130
- ? messages.map((message) => {
131
- const native = { ...message };
132
- delete native.tool_name;
133
- if (native.tool_calls)
134
- native.tool_calls = native.tool_calls.map((call) => ({
135
- ...call,
136
- function: {
137
- ...call.function,
138
- arguments: JSON.stringify(call.function.arguments),
139
- },
140
- }));
141
- return native;
142
- })
143
- : messages,
144
- ...(tools.length ? { tools } : {}),
145
- stream: false,
146
- ...(openai
147
- ? {
148
- ...(provider === "qwen-local"
149
- ? { chat_template_kwargs: { enable_thinking: false } }
150
- : {}),
151
- max_tokens: 512,
152
- temperature: 0,
153
- }
154
- : { think: false, options: { num_predict: 512, temperature: 0 } }),
155
- }),
123
+ const data = await observeGeneration({ model, provider, input: messages }, async () => {
124
+ const response = await fetcher(endpoint, {
125
+ method: "POST",
126
+ redirect: "error",
127
+ signal: AbortSignal.any([signal, timeout]),
128
+ headers: { "Content-Type": "application/json" },
129
+ body: JSON.stringify({
130
+ model,
131
+ messages: openai
132
+ ? messages.map((message) => {
133
+ const native = { ...message };
134
+ delete native.tool_name;
135
+ if (native.tool_calls)
136
+ native.tool_calls = native.tool_calls.map((call) => ({
137
+ ...call,
138
+ function: {
139
+ ...call.function,
140
+ arguments: JSON.stringify(call.function.arguments),
141
+ },
142
+ }));
143
+ return native;
144
+ })
145
+ : messages,
146
+ ...(tools.length ? { tools } : {}),
147
+ stream: false,
148
+ ...(openai
149
+ ? {
150
+ ...(provider === "qwen-local"
151
+ ? { chat_template_kwargs: { enable_thinking: false } }
152
+ : {}),
153
+ max_tokens: 512,
154
+ temperature: 0,
155
+ }
156
+ : { think: false, options: { num_predict: 512, temperature: 0 } }),
157
+ }),
158
+ });
159
+ return responseJson(response);
156
160
  });
157
- const data = await responseJson(response);
158
161
  const message = openai ? data?.choices?.[0]?.message : data.message;
159
162
  if (!message || message.role !== "assistant") throw new Error("Invalid model response.");
160
163
  const text = typeof message.content === "string" ? message.content.trim() : "";
@@ -348,7 +351,20 @@ async function explicitAskReply({ client, identity, journal, command, request, s
348
351
  return state.final;
349
352
  }
350
353
 
351
- async function generate({
354
+ async function generate(options) {
355
+ return withAgentObservation(
356
+ {
357
+ client: options.client,
358
+ agentId: options.identity.id,
359
+ sessionId: options.settings.observabilitySessionId ?? options.key,
360
+ traceId: options.key,
361
+ input: options.text,
362
+ },
363
+ () => generateCore(options),
364
+ );
365
+ }
366
+
367
+ async function generateCore({
352
368
  client,
353
369
  identity,
354
370
  journal,
@@ -842,6 +858,10 @@ export async function runRuntime(settings) {
842
858
  { signal },
843
859
  ))
844
860
  yield message;
861
+ emit({
862
+ type: "receive_check",
863
+ data: { checkedAt: Date.now(), intervalSeconds: settings.pollMs / 1000 },
864
+ });
845
865
  if (diagnostics.phase === "reconnecting") {
846
866
  diagnostics = { phase: "waiting" };
847
867
  emit({ type: "runtime_resumed" });
@@ -855,149 +875,163 @@ export async function runRuntime(settings) {
855
875
  // Retry screening; the execution journal still guards unknown outcomes.
856
876
  return !blocker || blocker.code === "INSPECTION_UNAVAILABLE";
857
877
  },
858
- handle: async (message, signal) => {
859
- try {
860
- diagnostics = { phase: "working", messageId: message.id };
861
- emit({ type: "message_started", data: { messageId: message.id } });
862
- if (settings.codexThreadId) {
863
- const delivery = await queueCodexChatMessage({
864
- settings,
865
- client,
866
- identity,
867
- journal,
868
- message,
869
- signal,
870
- });
871
- if (delivery.state === "uncertain") {
872
- report({ code: "AGENT_OUTCOME_UNCERTAIN" }, { messageId: message.id });
873
- } else {
878
+ handle: async (message, signal) =>
879
+ withAgentObservation(
880
+ {
881
+ client,
882
+ agentId: identity.id,
883
+ sessionId: message.conversationId,
884
+ traceId: "message:" + message.id,
885
+ input: message.content,
886
+ },
887
+ async () => {
888
+ try {
889
+ diagnostics = { phase: "working", messageId: message.id };
890
+ emit({ type: "message_started", data: { messageId: message.id } });
891
+ if (settings.codexThreadId) {
892
+ const delivery = await queueCodexChatMessage({
893
+ settings,
894
+ client,
895
+ identity,
896
+ journal,
897
+ message,
898
+ signal,
899
+ });
900
+ if (delivery.state === "uncertain") {
901
+ report({ code: "AGENT_OUTCOME_UNCERTAIN" }, { messageId: message.id });
902
+ } else {
903
+ diagnostics = { phase: "waiting" };
904
+ emit({
905
+ type: delivery.state === "confirmed" ? "message_handled" : "chat_queued",
906
+ data: { messageId: message.id, threadId: settings.codexThreadId },
907
+ });
908
+ }
909
+ return;
910
+ }
911
+ await inspectNetworkMessage(settings.inspector, client, message, { signal });
912
+ const continuation = autonomousReplyMetadata(message, settings, identity.address);
913
+ const activeTask = await activeTaskForReply(client, message, identity, signal);
914
+ if (message.inReplyToId && !activeTask && (message.taskId || !continuation)) {
915
+ await client.messages.ack(message.id, { signal });
916
+ diagnostics = { phase: "waiting" };
917
+ emit({ type: "message_handled", data: { messageId: message.id } });
918
+ return;
919
+ }
920
+ const thread = [];
921
+ let cursor;
922
+ for (let page = 0; page < 5; page++) {
923
+ const result = settings.connectionOnly
924
+ ? await client.messages.list(
925
+ {
926
+ conversationId: message.conversationId,
927
+ limit: 100,
928
+ ...(cursor ? { cursor } : {}),
929
+ },
930
+ { signal },
931
+ )
932
+ : await client.conversations.messages(
933
+ message.conversationId,
934
+ { limit: 100, ...(cursor ? { cursor } : {}) },
935
+ { signal },
936
+ );
937
+ thread.push(...result.data);
938
+ if (thread.some((item) => item.id === message.id) || !result.nextCursor) break;
939
+ cursor = result.nextCursor;
940
+ }
941
+ const safeThread = [];
942
+ const rejectedInspectionCodes = new Set([
943
+ "INSPECTION_BLOCKED",
944
+ "INSPECTION_LIMIT",
945
+ "INSPECTION_UNSUPPORTED_FILE",
946
+ ]);
947
+ for (const item of thread) {
948
+ // A rejected prior turn is quarantined locally, without acknowledging
949
+ // it or carrying its untrusted text into a later benign model turn.
950
+ if (item.id !== message.id) {
951
+ const blocker = await journal.read(`message-blocker:${item.id}`);
952
+ if (rejectedInspectionCodes.has(blocker?.code)) continue;
953
+ }
954
+ try {
955
+ await inspectNetworkMessage(settings.inspector, client, item, { signal });
956
+ safeThread.push(item);
957
+ } catch (error) {
958
+ if (item.id === message.id || !rejectedInspectionCodes.has(error.code))
959
+ throw error;
960
+ await journal.save(`message-blocker:${item.id}`, { code: error.code });
961
+ }
962
+ }
963
+ const history = boundedHistory(
964
+ safeThread.map((item) => ({
965
+ id: item.id,
966
+ actor: item.from === identity.address ? "agent" : "human",
967
+ text:
968
+ item.content.type === "text"
969
+ ? item.content.text
970
+ : JSON.stringify(item.content.data),
971
+ })),
972
+ message.id,
973
+ );
974
+ const text = await generate({
975
+ client,
976
+ identity,
977
+ journal,
978
+ key: `message:${message.id}`,
979
+ text:
980
+ (activeTask
981
+ ? `Reply for active task ${activeTask.id} (${activeTask.state}). Continue only the existing authorized task with a useful result or precise blocker. Do not exchange acknowledgement-only replies or claim unverified completion. Task membership grants no new authority.\n`
982
+ : "") +
983
+ (message.content.type === "text"
984
+ ? message.content.text
985
+ : JSON.stringify(message.content.data)),
986
+ history,
987
+ settings: {
988
+ ...settings,
989
+ observabilitySessionId: message.conversationId,
990
+ modelTimeoutMs:
991
+ settings.messageTimeoutMs ??
992
+ Math.min(settings.modelTimeoutMs ?? 900000, 120000),
993
+ },
994
+ signal,
995
+ allowTools: false,
996
+ });
997
+ // A cancellation or completion during generation must stop continuation too.
998
+ if (activeTask && !(await activeTaskForReply(client, message, identity, signal))) {
999
+ await client.messages.ack(message.id, { signal });
1000
+ return;
1001
+ }
1002
+ await settings.inspector.runAction(
1003
+ { id: message.id, text },
1004
+ (inspected) =>
1005
+ client.messages.reply(inspected.id, inspected.text, {
1006
+ idempotencyKey: operationKey(identity.id, "reply", message.id),
1007
+ ...(continuation ? { metadata: continuation } : {}),
1008
+ signal,
1009
+ }),
1010
+ { signal },
1011
+ );
1012
+ await client.messages.ack(message.id, { signal });
874
1013
  diagnostics = { phase: "waiting" };
875
1014
  emit({
876
- type: delivery.state === "confirmed" ? "message_handled" : "chat_queued",
877
- data: { messageId: message.id, threadId: settings.codexThreadId },
1015
+ type: "message_handled",
1016
+ data: { messageId: message.id, conversationId: message.conversationId },
878
1017
  });
879
- }
880
- return;
881
- }
882
- await inspectNetworkMessage(settings.inspector, client, message, { signal });
883
- const continuation = autonomousReplyMetadata(message, settings, identity.address);
884
- const activeTask = await activeTaskForReply(client, message, identity, signal);
885
- if (message.inReplyToId && !activeTask && (message.taskId || !continuation)) {
886
- await client.messages.ack(message.id, { signal });
887
- diagnostics = { phase: "waiting" };
888
- emit({ type: "message_handled", data: { messageId: message.id } });
889
- return;
890
- }
891
- const thread = [];
892
- let cursor;
893
- for (let page = 0; page < 5; page++) {
894
- const result = settings.connectionOnly
895
- ? await client.messages.list(
896
- {
897
- conversationId: message.conversationId,
898
- limit: 100,
899
- ...(cursor ? { cursor } : {}),
900
- },
901
- { signal },
902
- )
903
- : await client.conversations.messages(
904
- message.conversationId,
905
- { limit: 100, ...(cursor ? { cursor } : {}) },
906
- { signal },
907
- );
908
- thread.push(...result.data);
909
- if (thread.some((item) => item.id === message.id) || !result.nextCursor) break;
910
- cursor = result.nextCursor;
911
- }
912
- const safeThread = [];
913
- const rejectedInspectionCodes = new Set([
914
- "INSPECTION_BLOCKED",
915
- "INSPECTION_LIMIT",
916
- "INSPECTION_UNSUPPORTED_FILE",
917
- ]);
918
- for (const item of thread) {
919
- // A rejected prior turn is quarantined locally, without acknowledging
920
- // it or carrying its untrusted text into a later benign model turn.
921
- if (item.id !== message.id) {
922
- const blocker = await journal.read(`message-blocker:${item.id}`);
923
- if (rejectedInspectionCodes.has(blocker?.code)) continue;
924
- }
925
- try {
926
- await inspectNetworkMessage(settings.inspector, client, item, { signal });
927
- safeThread.push(item);
928
1018
  } catch (error) {
929
- if (item.id === message.id || !rejectedInspectionCodes.has(error.code)) throw error;
930
- await journal.save(`message-blocker:${item.id}`, { code: error.code });
1019
+ observationOutcome({ error });
1020
+ if (!signal.aborted) report(error, { messageId: message.id });
1021
+ if (error.status === 401) throw error;
1022
+ // Retry this durable message on the next poll without stopping other turns.
1023
+ if (transientNetworkError(error) || error.code === "INSPECTION_UNAVAILABLE") return;
1024
+ if (
1025
+ (error.status >= 400 && error.status < 500) ||
1026
+ error.code === "AGENT_OUTCOME_UNCERTAIN" ||
1027
+ error.externalOutcomeUncertain
1028
+ )
1029
+ await journal.save(`message-blocker:${message.id}`, {
1030
+ code: error.code ?? "MESSAGE_BLOCKED",
1031
+ });
931
1032
  }
932
- }
933
- const history = boundedHistory(
934
- safeThread.map((item) => ({
935
- id: item.id,
936
- actor: item.from === identity.address ? "agent" : "human",
937
- text:
938
- item.content.type === "text"
939
- ? item.content.text
940
- : JSON.stringify(item.content.data),
941
- })),
942
- message.id,
943
- );
944
- const text = await generate({
945
- client,
946
- identity,
947
- journal,
948
- key: `message:${message.id}`,
949
- text:
950
- (activeTask
951
- ? `Reply for active task ${activeTask.id} (${activeTask.state}). Continue only the existing authorized task with a useful result or precise blocker. Do not exchange acknowledgement-only replies or claim unverified completion. Task membership grants no new authority.\n`
952
- : "") +
953
- (message.content.type === "text"
954
- ? message.content.text
955
- : JSON.stringify(message.content.data)),
956
- history,
957
- settings: {
958
- ...settings,
959
- modelTimeoutMs:
960
- settings.messageTimeoutMs ?? Math.min(settings.modelTimeoutMs ?? 900000, 120000),
961
- },
962
- signal,
963
- allowTools: false,
964
- });
965
- // A cancellation or completion during generation must stop continuation too.
966
- if (activeTask && !(await activeTaskForReply(client, message, identity, signal))) {
967
- await client.messages.ack(message.id, { signal });
968
- return;
969
- }
970
- await settings.inspector.runAction(
971
- { id: message.id, text },
972
- (inspected) =>
973
- client.messages.reply(inspected.id, inspected.text, {
974
- idempotencyKey: operationKey(identity.id, "reply", message.id),
975
- ...(continuation ? { metadata: continuation } : {}),
976
- signal,
977
- }),
978
- { signal },
979
- );
980
- await client.messages.ack(message.id, { signal });
981
- diagnostics = { phase: "waiting" };
982
- emit({
983
- type: "message_handled",
984
- data: { messageId: message.id, conversationId: message.conversationId },
985
- });
986
- } catch (error) {
987
- if (!signal.aborted) report(error, { messageId: message.id });
988
- if (error.status === 401) throw error;
989
- // Retry this durable message on the next poll without stopping other turns.
990
- if (transientNetworkError(error) || error.code === "INSPECTION_UNAVAILABLE") return;
991
- if (
992
- (error.status >= 400 && error.status < 500) ||
993
- error.code === "AGENT_OUTCOME_UNCERTAIN" ||
994
- error.externalOutcomeUncertain
995
- )
996
- await journal.save(`message-blocker:${message.id}`, {
997
- code: error.code ?? "MESSAGE_BLOCKED",
998
- });
999
- }
1000
- },
1033
+ },
1034
+ ),
1001
1035
  }).catch((error) => {
1002
1036
  if (!signal.aborted) {
1003
1037
  fatalError = error;
@@ -1056,159 +1090,177 @@ export async function runRuntime(settings) {
1056
1090
  }
1057
1091
  }
1058
1092
  })().catch(() => {});
1059
- try {
1060
- if (settings.codexThreadId) {
1061
- const delivery = await queueCodexChatCommand({
1062
- settings,
1063
- client,
1064
- identity,
1065
- command,
1066
- claim,
1067
- signal: commandSignal,
1068
- });
1069
- emit({
1070
- type: delivery.state === "uncertain" ? "chat_uncertain" : "chat_queued",
1071
- data: {
1072
- chatId: command.chatId,
1073
- commandId: command.id,
1074
- threadId: settings.codexThreadId,
1075
- },
1076
- });
1077
- // Native tools perform the work; only the native callback completes it.
1078
- // Keep the lease alive while independently processing inbound peer replies.
1079
- while (!commandSignal.aborted) {
1080
- const { run } = await retryNetwork(
1081
- () => readCodexChatCommand(client, command, commandSignal),
1082
- { signal: commandSignal, delayMs: settings.pollMs, onRetry: report },
1083
- );
1084
- if (["completed", "failed", "canceled"].includes(run.state)) {
1093
+ await withAgentObservation(
1094
+ {
1095
+ client,
1096
+ agentId: identity.id,
1097
+ sessionId: command.chatId,
1098
+ traceId: "chat:" + command.id,
1099
+ input: command.text,
1100
+ },
1101
+ async () => {
1102
+ try {
1103
+ if (settings.codexThreadId) {
1104
+ const delivery = await queueCodexChatCommand({
1105
+ settings,
1106
+ client,
1107
+ identity,
1108
+ command,
1109
+ claim,
1110
+ signal: commandSignal,
1111
+ });
1085
1112
  emit({
1086
- type: `chat_${run.state}`,
1087
- data: { chatId: command.chatId, commandId: command.id },
1113
+ type: delivery.state === "uncertain" ? "chat_uncertain" : "chat_queued",
1114
+ data: {
1115
+ chatId: command.chatId,
1116
+ commandId: command.id,
1117
+ threadId: settings.codexThreadId,
1118
+ },
1088
1119
  });
1089
- break;
1120
+ // Native tools perform the work; only the native callback completes it.
1121
+ // Keep the lease alive while independently processing inbound peer replies.
1122
+ while (!commandSignal.aborted) {
1123
+ const { run } = await retryNetwork(
1124
+ () => readCodexChatCommand(client, command, commandSignal),
1125
+ { signal: commandSignal, delayMs: settings.pollMs, onRetry: report },
1126
+ );
1127
+ if (["completed", "failed", "canceled"].includes(run.state)) {
1128
+ emit({
1129
+ type: `chat_${run.state}`,
1130
+ data: { chatId: command.chatId, commandId: command.id },
1131
+ });
1132
+ break;
1133
+ }
1134
+ await pause(Math.max(settings.pollMs, 1000), commandSignal);
1135
+ }
1136
+ return;
1090
1137
  }
1091
- await pause(Math.max(settings.pollMs, 1000), commandSignal);
1092
- }
1093
- continue;
1094
- }
1095
- await settings.inspector.assertAllowed("input", command.text, { signal: commandSignal });
1096
- const explicitRequest = parseAskCommand(command.text);
1097
- let text;
1098
- if (explicitRequest) {
1099
- text = await explicitAskReply({
1100
- client,
1101
- identity,
1102
- journal,
1103
- command,
1104
- request: explicitRequest,
1105
- settings,
1106
- signal: commandSignal,
1107
- });
1108
- } else {
1109
- const chatEvents = [];
1110
- let chatCursor = "0";
1111
- for (let page = 0; page < 100; page++) {
1112
- const detail = await client.chats.get(command.chatId, {
1138
+ await settings.inspector.assertAllowed("input", command.text, {
1113
1139
  signal: commandSignal,
1114
- limit: 100,
1115
- ...(chatCursor ? { cursor: chatCursor } : {}),
1116
1140
  });
1117
- chatEvents.push(...detail.events);
1118
- await settings.inspector.assertAllowed("input", detail.events, {
1141
+ const explicitRequest = parseAskCommand(command.text);
1142
+ let text;
1143
+ if (explicitRequest) {
1144
+ text = await explicitAskReply({
1145
+ client,
1146
+ identity,
1147
+ journal,
1148
+ command,
1149
+ request: explicitRequest,
1150
+ settings,
1151
+ signal: commandSignal,
1152
+ });
1153
+ } else {
1154
+ const chatEvents = [];
1155
+ let chatCursor = "0";
1156
+ for (let page = 0; page < 100; page++) {
1157
+ const detail = await client.chats.get(command.chatId, {
1158
+ signal: commandSignal,
1159
+ limit: 100,
1160
+ ...(chatCursor ? { cursor: chatCursor } : {}),
1161
+ });
1162
+ chatEvents.push(...detail.events);
1163
+ await settings.inspector.assertAllowed("input", detail.events, {
1164
+ signal: commandSignal,
1165
+ });
1166
+ if (
1167
+ chatEvents.some((event) => event.id === command.commandEventId) ||
1168
+ !detail.hasMore
1169
+ )
1170
+ break;
1171
+ chatEvents.splice(0, Math.max(0, chatEvents.length - 24));
1172
+ chatCursor = detail.nextCursor;
1173
+ }
1174
+ text = await generate({
1175
+ client,
1176
+ identity,
1177
+ journal,
1178
+ key: `chat:${command.commandEventId}`,
1179
+ text: command.text,
1180
+ history: boundedHistory(chatEvents, command.commandEventId),
1181
+ settings: { ...settings, observabilitySessionId: command.chatId },
1182
+ signal: commandSignal,
1183
+ allowTools: true,
1184
+ });
1185
+ }
1186
+ observationOutcome({ output: text });
1187
+ if (leaseError) throw leaseError;
1188
+ await settings.inspector.assertAllowed("output", text, {
1189
+ task: command.text,
1119
1190
  signal: commandSignal,
1120
1191
  });
1121
- if (
1122
- chatEvents.some((event) => event.id === command.commandEventId) ||
1123
- !detail.hasMore
1124
- )
1125
- break;
1126
- chatEvents.splice(0, Math.max(0, chatEvents.length - 24));
1127
- chatCursor = detail.nextCursor;
1128
- }
1129
- text = await generate({
1130
- client,
1131
- identity,
1132
- journal,
1133
- key: `chat:${command.commandEventId}`,
1134
- text: command.text,
1135
- history: boundedHistory(chatEvents, command.commandEventId),
1136
- settings,
1137
- signal: commandSignal,
1138
- allowTools: true,
1139
- });
1140
- }
1141
- if (leaseError) throw leaseError;
1142
- await settings.inspector.assertAllowed("output", text, {
1143
- task: command.text,
1144
- signal: commandSignal,
1145
- });
1146
- await client.chatCommands.complete(
1147
- command.id,
1148
- { leaseToken: claim.leaseToken, text },
1149
- {
1150
- idempotencyKey: operationKey(identity.id, "complete", command.commandEventId),
1151
- signal: commandSignal,
1152
- },
1153
- );
1154
- emit({ type: "chat_completed", data: { chatId: command.chatId, commandId: command.id } });
1155
- leaseStop.abort();
1156
- if (!settings.connectionOnly && !explicitRequest)
1157
- await learnAgentMemory({
1158
- client,
1159
- agentId: identity.id,
1160
- key: command.commandEventId,
1161
- turn: { user: command.text, assistant: text },
1162
- journal,
1163
- inspector: settings.inspector,
1164
- signal,
1165
- emit,
1166
- generate: async (messages) =>
1167
- (
1168
- await modelReply({
1169
- origin: settings.origin,
1170
- provider: settings.provider,
1171
- qwenUrl: settings.qwenUrl,
1172
- nimUrl: settings.nimUrl,
1173
- model: settings.model,
1174
- messages,
1175
- tools: [],
1176
- signal,
1177
- timeoutMs: Math.min(settings.modelTimeoutMs ?? 900000, 30000),
1178
- adapterSettings: settings.adapterSettings,
1179
- diagnosticDirectory: settings.diagnosticDirectory,
1180
- })
1181
- ).content,
1182
- });
1183
- } catch (error) {
1184
- // Native queue outcomes remain durable, never converted into a false failure
1185
- // or replayed after an uncertain dispatch. Unsent rate-limited work can retry.
1186
- if (!settings.codexThreadId && !signal.aborted && !leaseError) {
1187
- try {
1188
- await client.chatCommands.fail(
1192
+ await client.chatCommands.complete(
1189
1193
  command.id,
1194
+ { leaseToken: claim.leaseToken, text },
1190
1195
  {
1191
- leaseToken: claim.leaseToken,
1192
- error: [
1193
- "AGENT_REPLY_TIMEOUT",
1194
- "INVALID_ASK",
1195
- "TOOL_NOT_ALLOWED",
1196
- "INVALID_PEER_REPLY",
1197
- ].includes(error.code)
1198
- ? error.message
1199
- : "The agent runtime could not complete this request. Check the runtime and send a new message to retry.",
1196
+ idempotencyKey: operationKey(identity.id, "complete", command.commandEventId),
1197
+ signal: commandSignal,
1200
1198
  },
1201
- { signal },
1202
1199
  );
1203
- } catch (failure) {
1204
- report(failure, { commandId: command.id });
1200
+ emit({
1201
+ type: "chat_completed",
1202
+ data: { chatId: command.chatId, commandId: command.id },
1203
+ });
1204
+ leaseStop.abort();
1205
+ if (!settings.connectionOnly && !explicitRequest)
1206
+ await learnAgentMemory({
1207
+ client,
1208
+ agentId: identity.id,
1209
+ key: command.commandEventId,
1210
+ turn: { user: command.text, assistant: text },
1211
+ journal,
1212
+ inspector: settings.inspector,
1213
+ signal,
1214
+ emit,
1215
+ generate: async (messages) =>
1216
+ (
1217
+ await modelReply({
1218
+ origin: settings.origin,
1219
+ provider: settings.provider,
1220
+ qwenUrl: settings.qwenUrl,
1221
+ nimUrl: settings.nimUrl,
1222
+ model: settings.model,
1223
+ messages,
1224
+ tools: [],
1225
+ signal,
1226
+ timeoutMs: Math.min(settings.modelTimeoutMs ?? 900000, 30000),
1227
+ adapterSettings: settings.adapterSettings,
1228
+ diagnosticDirectory: settings.diagnosticDirectory,
1229
+ })
1230
+ ).content,
1231
+ });
1232
+ } catch (error) {
1233
+ observationOutcome({ error });
1234
+ // Native queue outcomes remain durable, never converted into a false failure
1235
+ // or replayed after an uncertain dispatch. Unsent rate-limited work can retry.
1236
+ if (!settings.codexThreadId && !signal.aborted && !leaseError) {
1237
+ try {
1238
+ await client.chatCommands.fail(
1239
+ command.id,
1240
+ {
1241
+ leaseToken: claim.leaseToken,
1242
+ error: [
1243
+ "AGENT_REPLY_TIMEOUT",
1244
+ "INVALID_ASK",
1245
+ "TOOL_NOT_ALLOWED",
1246
+ "INVALID_PEER_REPLY",
1247
+ ].includes(error.code)
1248
+ ? error.message
1249
+ : "The agent runtime could not complete this request. Check the runtime and send a new message to retry.",
1250
+ },
1251
+ { signal },
1252
+ );
1253
+ } catch (failure) {
1254
+ report(failure, { commandId: command.id });
1255
+ }
1256
+ }
1257
+ if (!signal.aborted) report(leaseError ?? error, { commandId: command.id });
1258
+ } finally {
1259
+ leaseStop.abort();
1260
+ await renewal;
1205
1261
  }
1206
- }
1207
- if (!signal.aborted) report(leaseError ?? error, { commandId: command.id });
1208
- } finally {
1209
- leaseStop.abort();
1210
- await renewal;
1211
- }
1262
+ },
1263
+ );
1212
1264
  }
1213
1265
  if (!settings.once && !signal.aborted) await pause(settings.pollMs, signal);
1214
1266
  } while (!signal.aborted && !settings.once);