@ouro.bot/cli 0.1.0-alpha.4 → 0.1.0-alpha.40

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 (82) hide show
  1. package/AdoptionSpecialist.ouro/agent.json +70 -9
  2. package/AdoptionSpecialist.ouro/psyche/SOUL.md +5 -2
  3. package/AdoptionSpecialist.ouro/psyche/identities/monty.md +2 -2
  4. package/README.md +117 -188
  5. package/assets/ouroboros.png +0 -0
  6. package/changelog.json +161 -0
  7. package/dist/heart/config.js +81 -8
  8. package/dist/heart/core.js +78 -45
  9. package/dist/heart/daemon/agent-discovery.js +81 -0
  10. package/dist/heart/daemon/daemon-cli.js +987 -77
  11. package/dist/heart/daemon/daemon-entry.js +14 -5
  12. package/dist/heart/daemon/daemon-runtime-sync.js +90 -0
  13. package/dist/heart/daemon/daemon.js +177 -9
  14. package/dist/heart/daemon/hatch-animation.js +35 -0
  15. package/dist/heart/daemon/hatch-flow.js +4 -20
  16. package/dist/heart/daemon/hooks/bundle-meta.js +92 -0
  17. package/dist/heart/daemon/launchd.js +134 -0
  18. package/dist/heart/daemon/ouro-bot-entry.js +0 -0
  19. package/dist/heart/daemon/ouro-bot-global-installer.js +128 -0
  20. package/dist/heart/daemon/ouro-entry.js +0 -0
  21. package/dist/heart/daemon/ouro-path-installer.js +178 -0
  22. package/dist/heart/daemon/ouro-uti.js +11 -2
  23. package/dist/heart/daemon/process-manager.js +1 -1
  24. package/dist/heart/daemon/run-hooks.js +37 -0
  25. package/dist/heart/daemon/runtime-metadata.js +118 -0
  26. package/dist/heart/daemon/sense-manager.js +266 -0
  27. package/dist/heart/daemon/specialist-orchestrator.js +129 -0
  28. package/dist/heart/daemon/specialist-prompt.js +99 -0
  29. package/dist/heart/daemon/specialist-tools.js +283 -0
  30. package/dist/heart/daemon/staged-restart.js +114 -0
  31. package/dist/heart/daemon/subagent-installer.js +10 -1
  32. package/dist/heart/daemon/update-checker.js +103 -0
  33. package/dist/heart/daemon/update-hooks.js +138 -0
  34. package/dist/heart/daemon/wrapper-publish-guard.js +86 -0
  35. package/dist/heart/identity.js +96 -4
  36. package/dist/heart/kicks.js +1 -19
  37. package/dist/heart/providers/anthropic.js +16 -2
  38. package/dist/heart/sense-truth.js +61 -0
  39. package/dist/heart/streaming.js +96 -21
  40. package/dist/mind/bundle-manifest.js +70 -0
  41. package/dist/mind/context.js +7 -7
  42. package/dist/mind/first-impressions.js +2 -1
  43. package/dist/mind/friends/channel.js +43 -0
  44. package/dist/mind/friends/store-file.js +19 -0
  45. package/dist/mind/friends/types.js +9 -1
  46. package/dist/mind/pending.js +10 -2
  47. package/dist/mind/phrases.js +1 -0
  48. package/dist/mind/prompt.js +222 -7
  49. package/dist/mind/token-estimate.js +8 -12
  50. package/dist/nerves/cli-logging.js +15 -2
  51. package/dist/repertoire/ado-client.js +4 -2
  52. package/dist/repertoire/coding/feedback.js +134 -0
  53. package/dist/repertoire/coding/index.js +4 -1
  54. package/dist/repertoire/coding/manager.js +62 -4
  55. package/dist/repertoire/coding/spawner.js +3 -3
  56. package/dist/repertoire/coding/tools.js +41 -2
  57. package/dist/repertoire/data/ado-endpoints.json +188 -0
  58. package/dist/repertoire/tasks/index.js +2 -9
  59. package/dist/repertoire/tasks/transitions.js +1 -2
  60. package/dist/repertoire/tools-base.js +202 -219
  61. package/dist/repertoire/tools-bluebubbles.js +93 -0
  62. package/dist/repertoire/tools-teams.js +58 -25
  63. package/dist/repertoire/tools.js +55 -35
  64. package/dist/senses/bluebubbles-client.js +434 -0
  65. package/dist/senses/bluebubbles-entry.js +11 -0
  66. package/dist/senses/bluebubbles-media.js +338 -0
  67. package/dist/senses/bluebubbles-model.js +261 -0
  68. package/dist/senses/bluebubbles-mutation-log.js +74 -0
  69. package/dist/senses/bluebubbles-session-cleanup.js +72 -0
  70. package/dist/senses/bluebubbles.js +832 -0
  71. package/dist/senses/cli.js +327 -138
  72. package/dist/senses/debug-activity.js +127 -0
  73. package/dist/senses/inner-dialog.js +99 -54
  74. package/dist/senses/pipeline.js +124 -0
  75. package/dist/senses/teams.js +427 -112
  76. package/dist/senses/trust-gate.js +112 -2
  77. package/package.json +14 -3
  78. package/subagents/README.md +40 -53
  79. package/subagents/work-doer.md +26 -24
  80. package/subagents/work-merger.md +24 -30
  81. package/subagents/work-planner.md +34 -25
  82. package/dist/inner-worker-entry.js +0 -4
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createDebugActivityController = createDebugActivityController;
4
+ const format_1 = require("../mind/format");
5
+ const phrases_1 = require("../mind/phrases");
6
+ const runtime_1 = require("../nerves/runtime");
7
+ function createDebugActivityController(options) {
8
+ let queue = Promise.resolve();
9
+ let statusMessageGuid;
10
+ let typingActive = false;
11
+ let hadToolRun = false;
12
+ let followupShown = false;
13
+ let lastPhrase = "";
14
+ function reportTransportError(operation, error) {
15
+ (0, runtime_1.emitNervesEvent)({
16
+ level: "warn",
17
+ component: "senses",
18
+ event: "senses.debug_activity_transport_error",
19
+ message: "debug activity transport failed",
20
+ meta: {
21
+ operation,
22
+ reason: error instanceof Error ? error.message : String(error),
23
+ },
24
+ });
25
+ options.onTransportError?.(operation, error);
26
+ }
27
+ function enqueue(operation, task) {
28
+ queue = queue
29
+ .then(task)
30
+ .catch((error) => {
31
+ reportTransportError(operation, error);
32
+ });
33
+ }
34
+ function nextPhrase(pool) {
35
+ const phrase = (0, phrases_1.pickPhrase)(pool, lastPhrase);
36
+ lastPhrase = phrase;
37
+ return phrase;
38
+ }
39
+ function startTypingNow() {
40
+ if (typingActive) {
41
+ return;
42
+ }
43
+ typingActive = true;
44
+ enqueue("typing_start", async () => {
45
+ await options.transport.setTyping(true);
46
+ });
47
+ }
48
+ function setStatus(text) {
49
+ (0, runtime_1.emitNervesEvent)({
50
+ component: "senses",
51
+ event: "senses.debug_activity_update",
52
+ message: "debug activity status updated",
53
+ meta: {
54
+ hasStatusGuid: Boolean(statusMessageGuid),
55
+ textLength: text.length,
56
+ },
57
+ });
58
+ const shouldStartTyping = !typingActive;
59
+ if (shouldStartTyping) {
60
+ typingActive = true;
61
+ }
62
+ enqueue("status_update", async () => {
63
+ if (statusMessageGuid) {
64
+ await options.transport.editStatus(statusMessageGuid, text);
65
+ }
66
+ else {
67
+ statusMessageGuid = await options.transport.sendStatus(text);
68
+ }
69
+ if (shouldStartTyping) {
70
+ await options.transport.setTyping(true);
71
+ }
72
+ });
73
+ }
74
+ return {
75
+ onModelStart() {
76
+ const pool = hadToolRun ? options.followupPhrases : options.thinkingPhrases;
77
+ if (options.startTypingOnModelStart) {
78
+ startTypingNow();
79
+ }
80
+ if (options.suppressInitialModelStatus && !statusMessageGuid && !hadToolRun) {
81
+ nextPhrase(pool);
82
+ return;
83
+ }
84
+ setStatus(`${nextPhrase(pool)}...`);
85
+ },
86
+ onToolStart(name, args) {
87
+ hadToolRun = true;
88
+ followupShown = false;
89
+ const argSummary = Object.values(args).join(", ");
90
+ const detail = argSummary ? ` (${argSummary})` : "";
91
+ setStatus(`running ${name}${detail}...`);
92
+ },
93
+ onToolEnd(name, summary, success) {
94
+ hadToolRun = true;
95
+ followupShown = false;
96
+ setStatus((0, format_1.formatToolResult)(name, summary, success));
97
+ },
98
+ onTextChunk(text) {
99
+ if (!text || !hadToolRun || followupShown) {
100
+ return;
101
+ }
102
+ followupShown = true;
103
+ if (options.suppressFollowupPhraseStatus) {
104
+ return;
105
+ }
106
+ setStatus(`${nextPhrase(options.followupPhrases)}...`);
107
+ },
108
+ onError(error) {
109
+ setStatus((0, format_1.formatError)(error));
110
+ this.finish();
111
+ },
112
+ async drain() {
113
+ await queue;
114
+ },
115
+ async finish() {
116
+ if (!typingActive) {
117
+ await queue;
118
+ return;
119
+ }
120
+ typingActive = false;
121
+ enqueue("typing_stop", async () => {
122
+ await options.transport.setTyping(false);
123
+ });
124
+ await queue;
125
+ },
126
+ };
127
+ }
@@ -48,12 +48,17 @@ const identity_1 = require("../heart/identity");
48
48
  const context_1 = require("../mind/context");
49
49
  const prompt_1 = require("../mind/prompt");
50
50
  const bundle_manifest_1 = require("../mind/bundle-manifest");
51
+ const pending_1 = require("../mind/pending");
52
+ const channel_1 = require("../mind/friends/channel");
53
+ const trust_gate_1 = require("./trust-gate");
54
+ const tokens_1 = require("../mind/friends/tokens");
55
+ const pipeline_1 = require("./pipeline");
51
56
  const nerves_1 = require("../nerves");
52
57
  const runtime_1 = require("../nerves/runtime");
53
58
  const DEFAULT_INNER_DIALOG_INSTINCTS = [
54
59
  {
55
60
  id: "heartbeat_checkin",
56
- prompt: "Heartbeat instinct: check what changed, review priorities, and decide whether to keep resting or act.",
61
+ prompt: "...time passing. anything stirring?",
57
62
  enabled: true,
58
63
  },
59
64
  ];
@@ -68,19 +73,8 @@ function readAspirations(agentRoot) {
68
73
  function loadInnerDialogInstincts() {
69
74
  return [...DEFAULT_INNER_DIALOG_INSTINCTS];
70
75
  }
71
- function buildInnerDialogBootstrapMessage(aspirations, stateSummary) {
72
- const aspirationText = aspirations || "No explicit aspirations file found. Reflect and define what matters next.";
73
- return [
74
- "Inner dialog boot.",
75
- "",
76
- "## aspirations",
77
- aspirationText,
78
- "",
79
- "## current state",
80
- stateSummary,
81
- "",
82
- "Orient yourself, decide what to do next, and make meaningful progress.",
83
- ].join("\n");
76
+ function buildInnerDialogBootstrapMessage(_aspirations, _stateSummary) {
77
+ return "waking up. settling in.\n\nwhat needs my attention?";
84
78
  }
85
79
  function buildNonCanonicalCleanupNudge(nonCanonicalPaths) {
86
80
  if (nonCanonicalPaths.length === 0)
@@ -95,17 +89,14 @@ function buildNonCanonicalCleanupNudge(nonCanonicalPaths) {
95
89
  ...listed,
96
90
  ].join("\n");
97
91
  }
98
- function buildInstinctUserMessage(instincts, reason, state) {
92
+ function buildInstinctUserMessage(instincts, _reason, state) {
99
93
  const active = instincts.find((instinct) => instinct.enabled !== false) ?? DEFAULT_INNER_DIALOG_INSTINCTS[0];
100
- const checkpoint = state.checkpoint?.trim() || "no prior checkpoint recorded";
101
- return [
102
- active.prompt,
103
- `reason: ${reason}`,
104
- `cycle: ${state.cycleCount}`,
105
- `resting: ${state.resting ? "yes" : "no"}`,
106
- `checkpoint: ${checkpoint}`,
107
- "resume_instruction: continue from the checkpoint if still valid; otherwise revise and proceed.",
108
- ].join("\n");
94
+ const checkpoint = state.checkpoint?.trim();
95
+ const lines = [active.prompt];
96
+ if (checkpoint) {
97
+ lines.push(`\nlast i remember: ${checkpoint}`);
98
+ }
99
+ return lines.join("\n");
109
100
  }
110
101
  function contentToText(content) {
111
102
  if (typeof content === "string")
@@ -161,62 +152,116 @@ function createInnerDialogCallbacks() {
161
152
  };
162
153
  }
163
154
  function innerDialogSessionPath() {
164
- return (0, config_1.sessionPath)("self", "inner", "dialog");
155
+ return (0, config_1.sessionPath)(pending_1.INNER_DIALOG_PENDING.friendId, pending_1.INNER_DIALOG_PENDING.channel, pending_1.INNER_DIALOG_PENDING.key);
156
+ }
157
+ // Self-referencing friend record for inner dialog (agent talking to itself).
158
+ // No real friend to resolve -- this satisfies the pipeline's friend resolver contract.
159
+ function createSelfFriend(agentName) {
160
+ return {
161
+ id: "self",
162
+ name: agentName,
163
+ trustLevel: "family",
164
+ externalIds: [],
165
+ tenantMemberships: [],
166
+ toolPreferences: {},
167
+ notes: {},
168
+ totalTokens: 0,
169
+ createdAt: new Date().toISOString(),
170
+ updatedAt: new Date().toISOString(),
171
+ schemaVersion: 1,
172
+ };
173
+ }
174
+ // No-op friend store for inner dialog. Inner dialog doesn't track token usage per-friend.
175
+ function createNoOpFriendStore() {
176
+ return {
177
+ get: async () => null,
178
+ put: async () => { },
179
+ delete: async () => { },
180
+ findByExternalId: async () => null,
181
+ };
165
182
  }
166
183
  async function runInnerDialogTurn(options) {
167
184
  const now = options?.now ?? (() => new Date());
168
185
  const reason = options?.reason ?? "heartbeat";
169
186
  const sessionFilePath = innerDialogSessionPath();
170
187
  const loaded = (0, context_1.loadSession)(sessionFilePath);
171
- const messages = loaded?.messages ? [...loaded.messages] : [];
188
+ const existingMessages = loaded?.messages ? [...loaded.messages] : [];
172
189
  const instincts = options?.instincts ?? loadInnerDialogInstincts();
173
190
  const state = {
174
191
  cycleCount: 1,
175
192
  resting: false,
176
193
  lastHeartbeatAt: now().toISOString(),
177
194
  };
178
- if (messages.length === 0) {
179
- const systemPrompt = await (0, prompt_1.buildSystem)("cli", { toolChoiceRequired: true });
180
- messages.push({ role: "system", content: systemPrompt });
195
+ // ── Adapter concern: build user message ──────────────────────────
196
+ let userContent;
197
+ if (existingMessages.length === 0) {
198
+ // Fresh session: bootstrap message with non-canonical cleanup nudge
181
199
  const aspirations = readAspirations((0, identity_1.getAgentRoot)());
182
200
  const nonCanonical = (0, bundle_manifest_1.findNonCanonicalBundlePaths)((0, identity_1.getAgentRoot)());
183
201
  const cleanupNudge = buildNonCanonicalCleanupNudge(nonCanonical);
184
- const bootstrapMessage = [
202
+ userContent = [
185
203
  buildInnerDialogBootstrapMessage(aspirations, "No prior inner dialog session found."),
186
204
  cleanupNudge,
187
205
  ].filter(Boolean).join("\n\n");
188
- messages.push({ role: "user", content: bootstrapMessage });
189
206
  }
190
207
  else {
191
- const assistantTurns = messages.filter((message) => message.role === "assistant").length;
208
+ // Resumed session: instinct message with checkpoint context
209
+ const assistantTurns = existingMessages.filter((message) => message.role === "assistant").length;
192
210
  state.cycleCount = assistantTurns + 1;
193
- state.checkpoint = deriveResumeCheckpoint(messages);
194
- const instinctPrompt = buildInstinctUserMessage(instincts, reason, state);
195
- messages.push({ role: "user", content: instinctPrompt });
211
+ state.checkpoint = deriveResumeCheckpoint(existingMessages);
212
+ userContent = buildInstinctUserMessage(instincts, reason, state);
196
213
  }
214
+ // ── Adapter concern: inbox drain (inner-dialog-specific) ─────────
197
215
  const inboxMessages = options?.drainInbox?.() ?? [];
198
216
  if (inboxMessages.length > 0) {
199
- const lastUserIdx = messages.length - 1;
200
- const lastUser = messages[lastUserIdx];
201
- /* v8 ignore next -- defensive: all code paths push a user message before here @preserve */
202
- if (lastUser?.role === "user" && typeof lastUser.content === "string") {
203
- const section = inboxMessages
204
- .map((msg) => `- **${msg.from}**: ${msg.content}`)
205
- .join("\n");
206
- messages[lastUserIdx] = {
207
- ...lastUser,
208
- content: `${lastUser.content}\n\n## incoming messages\n${section}`,
209
- };
210
- }
217
+ const section = inboxMessages
218
+ .map((msg) => `- **${msg.from}**: ${msg.content}`)
219
+ .join("\n");
220
+ userContent = `${userContent}\n\n## incoming messages\n${section}`;
211
221
  }
222
+ const userMessage = { role: "user", content: userContent };
223
+ // ── Session loader: wraps existing session logic ──────────────────
224
+ const innerCapabilities = (0, channel_1.getChannelCapabilities)("inner");
225
+ const pendingDir = (0, pending_1.getInnerDialogPendingDir)((0, identity_1.getAgentName)());
226
+ const selfFriend = createSelfFriend((0, identity_1.getAgentName)());
227
+ const selfContext = { friend: selfFriend, channel: innerCapabilities };
228
+ const sessionLoader = {
229
+ loadOrCreate: async () => {
230
+ if (existingMessages.length > 0) {
231
+ return { messages: existingMessages, sessionPath: sessionFilePath };
232
+ }
233
+ // Fresh session: build system prompt
234
+ const systemPrompt = await (0, prompt_1.buildSystem)("inner", { toolChoiceRequired: true });
235
+ return {
236
+ messages: [{ role: "system", content: systemPrompt }],
237
+ sessionPath: sessionFilePath,
238
+ };
239
+ },
240
+ };
241
+ // ── Call shared pipeline ──────────────────────────────────────────
212
242
  const callbacks = createInnerDialogCallbacks();
213
243
  const traceId = (0, nerves_1.createTraceId)();
214
- const result = await (0, core_1.runAgent)(messages, callbacks, "cli", options?.signal, {
215
- traceId,
216
- toolChoiceRequired: true,
217
- skipConfirmation: true,
244
+ const result = await (0, pipeline_1.handleInboundTurn)({
245
+ channel: "inner",
246
+ capabilities: innerCapabilities,
247
+ messages: [userMessage],
248
+ callbacks,
249
+ friendResolver: { resolve: () => Promise.resolve(selfContext) },
250
+ sessionLoader,
251
+ pendingDir,
252
+ friendStore: createNoOpFriendStore(),
253
+ enforceTrustGate: trust_gate_1.enforceTrustGate,
254
+ drainPending: pending_1.drainPending,
255
+ runAgent: core_1.runAgent,
256
+ postTurn: context_1.postTurn,
257
+ accumulateFriendTokens: tokens_1.accumulateFriendTokens,
258
+ signal: options?.signal,
259
+ runAgentOptions: {
260
+ traceId,
261
+ toolChoiceRequired: true,
262
+ skipConfirmation: true,
263
+ },
218
264
  });
219
- (0, context_1.postTurn)(messages, sessionFilePath, result.usage);
220
265
  (0, runtime_1.emitNervesEvent)({
221
266
  component: "senses",
222
267
  event: "senses.inner_dialog_turn",
@@ -224,8 +269,8 @@ async function runInnerDialogTurn(options) {
224
269
  meta: { reason, session: sessionFilePath },
225
270
  });
226
271
  return {
227
- messages,
272
+ messages: result.messages ?? [],
228
273
  usage: result.usage,
229
- sessionPath: sessionFilePath,
274
+ sessionPath: result.sessionPath ?? sessionFilePath,
230
275
  };
231
276
  }
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ // Shared per-turn pipeline for all senses.
3
+ // Senses are thin transport adapters; this module owns the common lifecycle:
4
+ // resolve friend -> trust gate -> load session -> drain pending -> runAgent -> postTurn -> token accumulation.
5
+ //
6
+ // Transport-level concerns (BB API calls, Teams cards, readline) stay in sense adapters.
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.handleInboundTurn = handleInboundTurn;
9
+ const runtime_1 = require("../nerves/runtime");
10
+ // ── Pipeline ──────────────────────────────────────────────────────
11
+ async function handleInboundTurn(input) {
12
+ // Step 1: Resolve friend
13
+ const resolvedContext = await input.friendResolver.resolve();
14
+ (0, runtime_1.emitNervesEvent)({
15
+ component: "senses",
16
+ event: "senses.pipeline_start",
17
+ message: "inbound turn pipeline started",
18
+ meta: {
19
+ channel: input.channel,
20
+ friendId: resolvedContext.friend.id,
21
+ senseType: input.capabilities.senseType,
22
+ },
23
+ });
24
+ // Step 2: Trust gate
25
+ const gateInput = {
26
+ friend: resolvedContext.friend,
27
+ provider: input.provider ?? "local",
28
+ externalId: input.externalId ?? "",
29
+ tenantId: input.tenantId,
30
+ channel: input.channel,
31
+ senseType: input.capabilities.senseType,
32
+ isGroupChat: input.isGroupChat ?? false,
33
+ groupHasFamilyMember: input.groupHasFamilyMember ?? false,
34
+ hasExistingGroupWithFamily: input.hasExistingGroupWithFamily ?? false,
35
+ };
36
+ const gateResult = input.enforceTrustGate(gateInput);
37
+ // Gate rejection: return early, no agent turn
38
+ if (!gateResult.allowed) {
39
+ (0, runtime_1.emitNervesEvent)({
40
+ component: "senses",
41
+ event: "senses.pipeline_gate_reject",
42
+ message: "trust gate rejected inbound turn",
43
+ meta: {
44
+ channel: input.channel,
45
+ friendId: resolvedContext.friend.id,
46
+ reason: gateResult.reason,
47
+ },
48
+ });
49
+ return {
50
+ resolvedContext,
51
+ gateResult,
52
+ };
53
+ }
54
+ // Step 3: Load/create session
55
+ const session = await input.sessionLoader.loadOrCreate();
56
+ const sessionMessages = session.messages;
57
+ // Step 4: Drain pending messages
58
+ const pending = input.drainPending(input.pendingDir);
59
+ // Assemble messages: session messages + pending (formatted) + inbound user messages
60
+ if (pending.length > 0) {
61
+ // Format pending messages and prepend to the user content
62
+ const pendingSection = pending
63
+ .map((msg) => `[pending from ${msg.from}]: ${msg.content}`)
64
+ .join("\n");
65
+ // If there are inbound user messages, prepend pending to the first one
66
+ if (input.messages.length > 0) {
67
+ const firstMsg = input.messages[0];
68
+ if (firstMsg.role === "user") {
69
+ if (typeof firstMsg.content === "string") {
70
+ input.messages[0] = {
71
+ ...firstMsg,
72
+ content: `## pending messages\n${pendingSection}\n\n${firstMsg.content}`,
73
+ };
74
+ }
75
+ else {
76
+ input.messages[0] = {
77
+ ...firstMsg,
78
+ content: [
79
+ { type: "text", text: `## pending messages\n${pendingSection}\n\n` },
80
+ ...firstMsg.content,
81
+ ],
82
+ };
83
+ }
84
+ }
85
+ }
86
+ }
87
+ // Append user messages from the inbound turn
88
+ for (const msg of input.messages) {
89
+ sessionMessages.push(msg);
90
+ }
91
+ // Step 5: runAgent
92
+ const existingToolContext = input.runAgentOptions?.toolContext;
93
+ const runAgentOptions = {
94
+ ...input.runAgentOptions,
95
+ toolContext: {
96
+ /* v8 ignore next -- default no-op signin satisfies interface; real signin injected by sense adapter @preserve */
97
+ signin: async () => undefined,
98
+ ...existingToolContext,
99
+ context: resolvedContext,
100
+ friendStore: input.friendStore,
101
+ },
102
+ };
103
+ const result = await input.runAgent(sessionMessages, input.callbacks, input.channel, input.signal, runAgentOptions);
104
+ // Step 6: postTurn
105
+ input.postTurn(sessionMessages, session.sessionPath, result.usage);
106
+ // Step 7: Token accumulation
107
+ await input.accumulateFriendTokens(input.friendStore, resolvedContext.friend.id, result.usage);
108
+ (0, runtime_1.emitNervesEvent)({
109
+ component: "senses",
110
+ event: "senses.pipeline_end",
111
+ message: "inbound turn pipeline completed",
112
+ meta: {
113
+ channel: input.channel,
114
+ friendId: resolvedContext.friend.id,
115
+ },
116
+ });
117
+ return {
118
+ resolvedContext,
119
+ gateResult,
120
+ usage: result.usage,
121
+ sessionPath: session.sessionPath,
122
+ messages: sessionMessages,
123
+ };
124
+ }