@poncho-ai/cli 0.6.2 → 0.7.0

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/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  main
4
- } from "./chunk-EPG7ZYDE.js";
4
+ } from "./chunk-JIRFQFIX.js";
5
5
 
6
6
  // src/cli.ts
7
7
  void main();
package/dist/index.js CHANGED
@@ -23,7 +23,7 @@ import {
23
23
  runTests,
24
24
  startDevServer,
25
25
  updateAgentGuidance
26
- } from "./chunk-EPG7ZYDE.js";
26
+ } from "./chunk-JIRFQFIX.js";
27
27
  export {
28
28
  addSkill,
29
29
  buildCli,
@@ -0,0 +1,494 @@
1
+ import {
2
+ consumeFirstRunIntro,
3
+ inferConversationTitle,
4
+ resolveHarnessEnvironment
5
+ } from "./chunk-JIRFQFIX.js";
6
+
7
+ // src/run-interactive-ink.ts
8
+ import * as readline from "readline";
9
+ import { stdout } from "process";
10
+ import {
11
+ parseAgentFile
12
+ } from "@poncho-ai/harness";
13
+ var C = {
14
+ reset: "\x1B[0m",
15
+ bold: "\x1B[1m",
16
+ dim: "\x1B[2m",
17
+ cyan: "\x1B[36m",
18
+ green: "\x1B[32m",
19
+ yellow: "\x1B[33m",
20
+ red: "\x1B[31m",
21
+ gray: "\x1B[90m",
22
+ magenta: "\x1B[35m"
23
+ };
24
+ var green = (s) => `${C.green}${s}${C.reset}`;
25
+ var yellow = (s) => `${C.yellow}${s}${C.reset}`;
26
+ var red = (s) => `${C.red}${s}${C.reset}`;
27
+ var gray = (s) => `${C.gray}${s}${C.reset}`;
28
+ var magenta = (s) => `${C.magenta}${s}${C.reset}`;
29
+ var FAUX_TOOL_LOG_PATTERN = /Tool Used:|Tool Result:|\blist_skills\b|\bcreate_skill\b|\bedit_skill\b/i;
30
+ var formatDuration = (ms) => ms < 1e3 ? `${ms}ms` : `${(ms / 1e3).toFixed(1)}s`;
31
+ var stringifyValue = (v) => {
32
+ try {
33
+ return JSON.stringify(v);
34
+ } catch {
35
+ return String(v);
36
+ }
37
+ };
38
+ var truncate = (v, max) => v.length <= max ? v : `${v.slice(0, Math.max(0, max - 3))}...`;
39
+ var compactPreview = (v, max = 120) => truncate(stringifyValue(v).replace(/\s+/g, " "), max);
40
+ var loadMetadata = async (workingDir) => {
41
+ let agentName = "agent";
42
+ let model = "unknown";
43
+ let provider = "unknown";
44
+ try {
45
+ const parsed = await parseAgentFile(workingDir);
46
+ agentName = parsed.frontmatter.name ?? agentName;
47
+ model = parsed.frontmatter.model?.name ?? model;
48
+ provider = parsed.frontmatter.model?.provider ?? provider;
49
+ } catch {
50
+ }
51
+ return {
52
+ agentName,
53
+ model,
54
+ provider,
55
+ workingDir,
56
+ environment: resolveHarnessEnvironment()
57
+ };
58
+ };
59
+ var ask = (rl, prompt) => new Promise((res) => {
60
+ rl.question(prompt, (answer) => res(answer));
61
+ });
62
+ var sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
63
+ var streamTextAsTokens = async (text) => {
64
+ const tokens = text.match(/\S+\s*|\n/g) ?? [text];
65
+ for (const token of tokens) {
66
+ stdout.write(token);
67
+ const trimmed = token.trim();
68
+ const delay = trimmed.length === 0 ? 0 : Math.max(4, Math.min(18, Math.floor(trimmed.length / 2)));
69
+ await sleep(delay);
70
+ }
71
+ };
72
+ var OWNER_ID = "local-owner";
73
+ var computeTurn = (messages) => Math.max(1, Math.floor(messages.length / 2) + 1);
74
+ var formatDate = (value) => {
75
+ try {
76
+ return new Date(value).toLocaleString();
77
+ } catch {
78
+ return String(value);
79
+ }
80
+ };
81
+ var handleSlash = async (command, state, conversationStore) => {
82
+ const [rawCommand, ...args] = command.trim().split(/\s+/);
83
+ const norm = rawCommand.toLowerCase();
84
+ if (norm === "/help") {
85
+ console.log(
86
+ gray(
87
+ "commands> /help /clear /exit /tools /list /open <id> /new [title] /delete [id] /continue /reset [all]"
88
+ )
89
+ );
90
+ return { shouldExit: false };
91
+ }
92
+ if (norm === "/clear") {
93
+ process.stdout.write("\x1B[2J\x1B[H");
94
+ return { shouldExit: false };
95
+ }
96
+ if (norm === "/exit") {
97
+ return { shouldExit: true };
98
+ }
99
+ if (norm === "/list") {
100
+ const conversations = await conversationStore.list(OWNER_ID);
101
+ if (conversations.length === 0) {
102
+ console.log(gray("conversations> none"));
103
+ return { shouldExit: false };
104
+ }
105
+ console.log(gray("conversations>"));
106
+ for (const conversation of conversations) {
107
+ const activeMarker = state.activeConversationId === conversation.conversationId ? "*" : " ";
108
+ console.log(
109
+ gray(
110
+ `${activeMarker} ${conversation.conversationId} | ${conversation.title} | ${formatDate(conversation.updatedAt)}`
111
+ )
112
+ );
113
+ }
114
+ return { shouldExit: false };
115
+ }
116
+ if (norm === "/open") {
117
+ const conversationId = args[0];
118
+ if (!conversationId) {
119
+ console.log(yellow("usage> /open <conversationId>"));
120
+ return { shouldExit: false };
121
+ }
122
+ const conversation = await conversationStore.get(conversationId);
123
+ if (!conversation) {
124
+ console.log(yellow(`conversations> not found: ${conversationId}`));
125
+ return { shouldExit: false };
126
+ }
127
+ state.activeConversationId = conversation.conversationId;
128
+ state.messages = [...conversation.messages];
129
+ state.turn = computeTurn(state.messages);
130
+ console.log(gray(`conversations> opened ${conversation.conversationId}`));
131
+ return { shouldExit: false };
132
+ }
133
+ if (norm === "/new") {
134
+ const title = args.join(" ").trim();
135
+ const conversation = await conversationStore.create(OWNER_ID, title || void 0);
136
+ state.activeConversationId = conversation.conversationId;
137
+ state.messages = [];
138
+ state.turn = 1;
139
+ console.log(gray(`conversations> new ${conversation.conversationId}`));
140
+ return { shouldExit: false };
141
+ }
142
+ if (norm === "/delete") {
143
+ const targetConversationId = args[0] ?? state.activeConversationId ?? "";
144
+ if (!targetConversationId) {
145
+ console.log(yellow("usage> /delete <conversationId>"));
146
+ return { shouldExit: false };
147
+ }
148
+ const removed = await conversationStore.delete(targetConversationId);
149
+ if (!removed) {
150
+ console.log(yellow(`conversations> not found: ${targetConversationId}`));
151
+ return { shouldExit: false };
152
+ }
153
+ if (state.activeConversationId === targetConversationId) {
154
+ state.activeConversationId = null;
155
+ state.messages = [];
156
+ state.turn = 1;
157
+ }
158
+ console.log(gray(`conversations> deleted ${targetConversationId}`));
159
+ return { shouldExit: false };
160
+ }
161
+ if (norm === "/continue") {
162
+ const conversations = await conversationStore.list(OWNER_ID);
163
+ const latest = conversations[0];
164
+ if (!latest) {
165
+ console.log(yellow("conversations> no conversations to continue"));
166
+ return { shouldExit: false };
167
+ }
168
+ state.activeConversationId = latest.conversationId;
169
+ state.messages = [...latest.messages];
170
+ state.turn = computeTurn(state.messages);
171
+ console.log(gray(`conversations> continued ${latest.conversationId}`));
172
+ return { shouldExit: false };
173
+ }
174
+ if (norm === "/reset") {
175
+ if (args[0]?.toLowerCase() === "all") {
176
+ const conversations = await conversationStore.list(OWNER_ID);
177
+ for (const conversation2 of conversations) {
178
+ await conversationStore.delete(conversation2.conversationId);
179
+ }
180
+ state.activeConversationId = null;
181
+ state.messages = [];
182
+ state.turn = 1;
183
+ console.log(gray("conversations> reset all"));
184
+ return { shouldExit: false };
185
+ }
186
+ if (!state.activeConversationId) {
187
+ state.messages = [];
188
+ state.turn = 1;
189
+ console.log(gray("conversations> current session reset"));
190
+ return { shouldExit: false };
191
+ }
192
+ const conversation = await conversationStore.get(state.activeConversationId);
193
+ if (!conversation) {
194
+ state.activeConversationId = null;
195
+ state.messages = [];
196
+ state.turn = 1;
197
+ console.log(yellow("conversations> active conversation no longer exists"));
198
+ return { shouldExit: false };
199
+ }
200
+ await conversationStore.update({
201
+ ...conversation,
202
+ messages: []
203
+ });
204
+ state.messages = [];
205
+ state.turn = 1;
206
+ console.log(gray(`conversations> reset ${conversation.conversationId}`));
207
+ return { shouldExit: false };
208
+ }
209
+ console.log(yellow(`Unknown command: ${command}`));
210
+ return { shouldExit: false };
211
+ };
212
+ var runInteractiveInk = async ({
213
+ harness,
214
+ params,
215
+ workingDir,
216
+ config,
217
+ conversationStore,
218
+ onSetApprovalCallback
219
+ }) => {
220
+ const metadata = await loadMetadata(workingDir);
221
+ const rl = readline.createInterface({
222
+ input: process.stdin,
223
+ output: process.stdout,
224
+ terminal: true
225
+ });
226
+ if (onSetApprovalCallback) {
227
+ onSetApprovalCallback((req) => {
228
+ process.stdout.write("\n");
229
+ const preview = compactPreview(req.input, 100);
230
+ rl.question(
231
+ `${C.yellow}${C.bold}Tool "${req.tool}" requires approval${C.reset}
232
+ ${C.gray}input: ${preview}${C.reset}
233
+ ${C.yellow}approve? (y/n): ${C.reset}`,
234
+ (answer) => {
235
+ const approved = answer.trim().toLowerCase() === "y";
236
+ console.log(
237
+ approved ? green(` approved ${req.tool}`) : magenta(` denied ${req.tool}`)
238
+ );
239
+ req.resolve(approved);
240
+ }
241
+ );
242
+ });
243
+ }
244
+ console.log(
245
+ gray(
246
+ `
247
+ ${metadata.agentName} | ${metadata.provider}/${metadata.model} | ${metadata.environment}`
248
+ )
249
+ );
250
+ console.log(gray('Type "exit" to quit, "/help" for commands'));
251
+ console.log(
252
+ gray("Conversation controls: /list /open <id> /new [title] /delete [id] /continue /reset [all]\n")
253
+ );
254
+ const intro = await consumeFirstRunIntro(workingDir, {
255
+ agentName: metadata.agentName,
256
+ provider: metadata.provider,
257
+ model: metadata.model,
258
+ config
259
+ });
260
+ if (intro) {
261
+ console.log(green("assistant>"));
262
+ await streamTextAsTokens(intro);
263
+ stdout.write("\n\n");
264
+ }
265
+ let messages = intro ? [{ role: "assistant", content: intro }] : [];
266
+ let turn = 1;
267
+ let activeConversationId = null;
268
+ let showToolPayloads = false;
269
+ const prompt = `${C.cyan}you> ${C.reset}`;
270
+ while (true) {
271
+ let task;
272
+ try {
273
+ task = await ask(rl, prompt);
274
+ } catch {
275
+ break;
276
+ }
277
+ const trimmed = task.trim();
278
+ if (!trimmed) continue;
279
+ if (trimmed.toLowerCase() === "exit") break;
280
+ if (trimmed.startsWith("/")) {
281
+ if (trimmed.toLowerCase() === "/exit") break;
282
+ if (trimmed.toLowerCase() === "/tools") {
283
+ showToolPayloads = !showToolPayloads;
284
+ console.log(gray(`tool payloads: ${showToolPayloads ? "on" : "off"}`));
285
+ continue;
286
+ }
287
+ const interactiveState = {
288
+ messages,
289
+ turn,
290
+ activeConversationId
291
+ };
292
+ const slashResult = await handleSlash(
293
+ trimmed,
294
+ interactiveState,
295
+ conversationStore
296
+ );
297
+ if (slashResult.shouldExit) {
298
+ break;
299
+ }
300
+ messages = interactiveState.messages;
301
+ turn = interactiveState.turn;
302
+ activeConversationId = interactiveState.activeConversationId;
303
+ continue;
304
+ }
305
+ console.log(gray(`
306
+ --- turn ${turn} ---`));
307
+ process.stdout.write(gray("thinking..."));
308
+ let thinkingCleared = false;
309
+ const clearThinking = () => {
310
+ if (thinkingCleared) return;
311
+ thinkingCleared = true;
312
+ readline.clearLine(process.stdout, 0);
313
+ readline.cursorTo(process.stdout, 0);
314
+ };
315
+ let responseText = "";
316
+ let streamedText = "";
317
+ let committedText = false;
318
+ let sawChunk = false;
319
+ let toolEvents = 0;
320
+ const toolTimeline = [];
321
+ const sections = [];
322
+ let currentText = "";
323
+ let currentTools = [];
324
+ let runFailed = false;
325
+ let usage;
326
+ let latestRunId = "";
327
+ const startedAt = Date.now();
328
+ try {
329
+ for await (const event of harness.run({
330
+ task: trimmed,
331
+ parameters: params,
332
+ messages
333
+ })) {
334
+ if (event.type === "run:started") {
335
+ latestRunId = event.runId;
336
+ }
337
+ if (event.type === "model:chunk") {
338
+ sawChunk = true;
339
+ if (currentTools.length > 0) {
340
+ sections.push({ type: "tools", content: currentTools });
341
+ currentTools = [];
342
+ }
343
+ responseText += event.content;
344
+ streamedText += event.content;
345
+ currentText += event.content;
346
+ if (!thinkingCleared) {
347
+ clearThinking();
348
+ process.stdout.write(`${C.green}assistant> ${C.reset}`);
349
+ }
350
+ process.stdout.write(event.content);
351
+ } else if (event.type === "tool:started" || event.type === "tool:completed" || event.type === "tool:error" || event.type === "tool:approval:required" || event.type === "tool:approval:granted" || event.type === "tool:approval:denied") {
352
+ if (streamedText.length > 0) {
353
+ committedText = true;
354
+ streamedText = "";
355
+ process.stdout.write("\n");
356
+ }
357
+ clearThinking();
358
+ if (event.type === "tool:started") {
359
+ if (currentText.length > 0) {
360
+ sections.push({ type: "text", content: currentText });
361
+ currentText = "";
362
+ }
363
+ const preview = showToolPayloads ? compactPreview(event.input, 400) : compactPreview(event.input, 100);
364
+ console.log(yellow(`tools> start ${event.tool} input=${preview}`));
365
+ const toolText = `- start \`${event.tool}\``;
366
+ toolTimeline.push(toolText);
367
+ currentTools.push(toolText);
368
+ toolEvents += 1;
369
+ } else if (event.type === "tool:completed") {
370
+ const preview = showToolPayloads ? compactPreview(event.output, 400) : compactPreview(event.output, 100);
371
+ console.log(
372
+ yellow(
373
+ `tools> done ${event.tool} in ${formatDuration(event.duration)}`
374
+ )
375
+ );
376
+ if (showToolPayloads) {
377
+ console.log(yellow(`tools> output ${preview}`));
378
+ }
379
+ const toolText = `- done \`${event.tool}\` in ${formatDuration(event.duration)}`;
380
+ toolTimeline.push(toolText);
381
+ currentTools.push(toolText);
382
+ } else if (event.type === "tool:error") {
383
+ console.log(
384
+ red(`tools> error ${event.tool}: ${event.error}`)
385
+ );
386
+ const toolText = `- error \`${event.tool}\`: ${event.error}`;
387
+ toolTimeline.push(toolText);
388
+ currentTools.push(toolText);
389
+ } else if (event.type === "tool:approval:required") {
390
+ console.log(
391
+ magenta(`tools> approval required for ${event.tool}`)
392
+ );
393
+ const toolText = `- approval required \`${event.tool}\``;
394
+ toolTimeline.push(toolText);
395
+ currentTools.push(toolText);
396
+ } else if (event.type === "tool:approval:granted") {
397
+ console.log(
398
+ gray(`tools> approval granted (${event.approvalId})`)
399
+ );
400
+ const toolText = `- approval granted (${event.approvalId})`;
401
+ toolTimeline.push(toolText);
402
+ currentTools.push(toolText);
403
+ } else if (event.type === "tool:approval:denied") {
404
+ console.log(
405
+ magenta(`tools> approval denied (${event.approvalId})`)
406
+ );
407
+ const toolText = `- approval denied (${event.approvalId})`;
408
+ toolTimeline.push(toolText);
409
+ currentTools.push(toolText);
410
+ }
411
+ } else if (event.type === "run:error") {
412
+ clearThinking();
413
+ runFailed = true;
414
+ console.log(red(`error> ${event.error.message}`));
415
+ } else if (event.type === "model:response") {
416
+ usage = event.usage;
417
+ } else if (event.type === "run:completed" && !sawChunk) {
418
+ clearThinking();
419
+ responseText = event.result.response ?? "";
420
+ if (responseText.length > 0) {
421
+ process.stdout.write(
422
+ `${C.green}assistant> ${C.reset}${responseText}
423
+ `
424
+ );
425
+ }
426
+ }
427
+ }
428
+ } catch (error) {
429
+ clearThinking();
430
+ runFailed = true;
431
+ console.log(
432
+ red(
433
+ `error> ${error instanceof Error ? error.message : "Unknown error"}`
434
+ )
435
+ );
436
+ }
437
+ if (sawChunk && streamedText.length > 0) {
438
+ process.stdout.write("\n");
439
+ } else if (!sawChunk && !runFailed && responseText.length === 0) {
440
+ clearThinking();
441
+ console.log(green("assistant> (no response)"));
442
+ }
443
+ const fullResponse = responseText || streamedText;
444
+ if (!runFailed && toolEvents === 0 && FAUX_TOOL_LOG_PATTERN.test(fullResponse)) {
445
+ console.log(
446
+ magenta(
447
+ "warning> assistant described tool execution but no real tool events occurred."
448
+ )
449
+ );
450
+ }
451
+ const durationMs = Date.now() - startedAt;
452
+ console.log(
453
+ gray(`meta> ${formatDuration(durationMs)} | tools: ${toolEvents}
454
+ `)
455
+ );
456
+ if (!activeConversationId) {
457
+ const created = await conversationStore.create(
458
+ OWNER_ID,
459
+ inferConversationTitle(trimmed)
460
+ );
461
+ activeConversationId = created.conversationId;
462
+ }
463
+ if (currentTools.length > 0) {
464
+ sections.push({ type: "tools", content: currentTools });
465
+ }
466
+ if (currentText.length > 0) {
467
+ sections.push({ type: "text", content: currentText });
468
+ }
469
+ messages.push({ role: "user", content: trimmed });
470
+ messages.push({
471
+ role: "assistant",
472
+ content: responseText,
473
+ metadata: toolTimeline.length > 0 || sections.length > 0 ? {
474
+ toolActivity: toolTimeline,
475
+ sections: sections.length > 0 ? sections : void 0
476
+ } : void 0
477
+ });
478
+ turn = computeTurn(messages);
479
+ const conversation = await conversationStore.get(activeConversationId);
480
+ if (conversation) {
481
+ const maybeTitle = conversation.messages.length === 0 && (conversation.title === "New conversation" || conversation.title.trim().length === 0) ? inferConversationTitle(trimmed) : conversation.title;
482
+ await conversationStore.update({
483
+ ...conversation,
484
+ title: maybeTitle,
485
+ messages: [...messages],
486
+ runtimeRunId: latestRunId || conversation.runtimeRunId
487
+ });
488
+ }
489
+ }
490
+ rl.close();
491
+ };
492
+ export {
493
+ runInteractiveInk
494
+ };
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@poncho-ai/cli",
3
- "version": "0.6.2",
3
+ "version": "0.7.0",
4
4
  "description": "CLI for building and deploying AI agents",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "https://github.com/latitude-dev/poncho-ai.git",
7
+ "url": "https://github.com/cesr/poncho-ai.git",
8
8
  "directory": "packages/cli"
9
9
  },
10
10
  "publishConfig": {
@@ -26,8 +26,8 @@
26
26
  "react": "^19.2.4",
27
27
  "react-devtools-core": "^6.1.5",
28
28
  "yaml": "^2.8.1",
29
- "@poncho-ai/harness": "0.7.1",
30
- "@poncho-ai/sdk": "0.2.0"
29
+ "@poncho-ai/harness": "0.8.0",
30
+ "@poncho-ai/sdk": "0.3.0"
31
31
  },
32
32
  "devDependencies": {
33
33
  "@types/react": "^19.2.14",
package/src/index.ts CHANGED
@@ -15,6 +15,8 @@ import {
15
15
  LocalMcpBridge,
16
16
  TelemetryEmitter,
17
17
  createConversationStore,
18
+ ensureAgentIdentity,
19
+ generateAgentId,
18
20
  loadPonchoConfig,
19
21
  resolveStateConfig,
20
22
  type PonchoConfig,
@@ -201,9 +203,11 @@ const parseParams = (values: string[]): Record<string, string> => {
201
203
 
202
204
  const AGENT_TEMPLATE = (
203
205
  name: string,
206
+ id: string,
204
207
  options: { modelProvider: "anthropic" | "openai"; modelName: string },
205
208
  ): string => `---
206
209
  name: ${name}
210
+ id: ${id}
207
211
  description: A helpful Poncho assistant
208
212
  model:
209
213
  provider: ${options.modelProvider}
@@ -727,6 +731,7 @@ export const initProject = async (
727
731
  interactive: false,
728
732
  };
729
733
  const onboarding = await runInitOnboarding(onboardingOptions);
734
+ const agentId = generateAgentId();
730
735
 
731
736
  const G = "\x1b[32m";
732
737
  const D = "\x1b[2m";
@@ -738,7 +743,13 @@ export const initProject = async (
738
743
  process.stdout.write("\n");
739
744
 
740
745
  const scaffoldFiles: Array<{ path: string; content: string }> = [
741
- { path: "AGENT.md", content: AGENT_TEMPLATE(projectName, { modelProvider: onboarding.agentModel.provider, modelName: onboarding.agentModel.name }) },
746
+ {
747
+ path: "AGENT.md",
748
+ content: AGENT_TEMPLATE(projectName, agentId, {
749
+ modelProvider: onboarding.agentModel.provider,
750
+ modelName: onboarding.agentModel.name,
751
+ }),
752
+ },
742
753
  { path: "poncho.config.js", content: renderConfigFile(onboarding.config) },
743
754
  { path: "package.json", content: PACKAGE_TEMPLATE(projectName, projectDir) },
744
755
  { path: "README.md", content: README_TEMPLATE(projectName) },
@@ -935,7 +946,11 @@ export const createRequestHandler = async (options?: {
935
946
  });
936
947
  await harness.initialize();
937
948
  const telemetry = new TelemetryEmitter(config?.telemetry);
938
- const conversationStore = createConversationStore(resolveStateConfig(config), { workingDir });
949
+ const identity = await ensureAgentIdentity(workingDir);
950
+ const conversationStore = createConversationStore(resolveStateConfig(config), {
951
+ workingDir,
952
+ agentId: identity.id,
953
+ });
939
954
  const sessionStore = new SessionStore();
940
955
  const loginRateLimiter = new LoginRateLimiter();
941
956
 
@@ -1688,6 +1703,7 @@ export const runInteractive = async (
1688
1703
  approvalHandler,
1689
1704
  });
1690
1705
  await harness.initialize();
1706
+ const identity = await ensureAgentIdentity(workingDir);
1691
1707
  try {
1692
1708
  const { runInteractiveInk } = await import("./run-interactive-ink.js");
1693
1709
  await (
@@ -1704,7 +1720,10 @@ export const runInteractive = async (
1704
1720
  params,
1705
1721
  workingDir,
1706
1722
  config,
1707
- conversationStore: createConversationStore(resolveStateConfig(config), { workingDir }),
1723
+ conversationStore: createConversationStore(resolveStateConfig(config), {
1724
+ workingDir,
1725
+ agentId: identity.id,
1726
+ }),
1708
1727
  onSetApprovalCallback: (cb: (req: ApprovalRequest) => void) => {
1709
1728
  onApprovalRequest = cb;
1710
1729
  // If there's already a pending request, fire it immediately
@@ -1,8 +1,10 @@
1
- import { createHash } from "node:crypto";
2
1
  import { access, mkdir, readFile, writeFile } from "node:fs/promises";
3
- import { basename, dirname, resolve } from "node:path";
4
- import { homedir } from "node:os";
5
- import type { PonchoConfig } from "@poncho-ai/harness";
2
+ import { dirname, resolve } from "node:path";
3
+ import {
4
+ ensureAgentIdentity,
5
+ getAgentStoreDirectory,
6
+ type PonchoConfig,
7
+ } from "@poncho-ai/harness";
6
8
  import { resolveHarnessEnvironment } from "./index.js";
7
9
 
8
10
  type IntroInput = {
@@ -22,26 +24,6 @@ type OnboardingMarkerState = {
22
24
 
23
25
  const ONBOARDING_VERSION = 1;
24
26
 
25
- const getStateDirectory = (): string => {
26
- const cwd = process.cwd();
27
- const home = homedir();
28
- const isServerless =
29
- process.env.VERCEL === "1" ||
30
- process.env.VERCEL_ENV !== undefined ||
31
- process.env.VERCEL_URL !== undefined ||
32
- process.env.AWS_LAMBDA_FUNCTION_NAME !== undefined ||
33
- process.env.AWS_EXECUTION_ENV?.includes("AWS_Lambda") === true ||
34
- process.env.LAMBDA_TASK_ROOT !== undefined ||
35
- process.env.NOW_REGION !== undefined ||
36
- cwd.startsWith("/var/task") ||
37
- home.startsWith("/var/task") ||
38
- process.env.SERVERLESS === "1";
39
- if (isServerless) {
40
- return "/tmp/.poncho/state";
41
- }
42
- return resolve(homedir(), ".poncho", "state");
43
- };
44
-
45
27
  const summarizeConfig = (config: PonchoConfig | undefined): string[] => {
46
28
  const provider = config?.storage?.provider ?? config?.state?.provider ?? "local";
47
29
  const memoryEnabled = config?.storage?.memory?.enabled ?? config?.memory?.enabled ?? false;
@@ -55,19 +37,15 @@ const summarizeConfig = (config: PonchoConfig | undefined): string[] => {
55
37
  ];
56
38
  };
57
39
 
58
- const getOnboardingMarkerPath = (workingDir: string): string =>
59
- resolve(
60
- getStateDirectory(),
61
- `${basename(workingDir).replace(/[^a-zA-Z0-9_-]+/g, "-") || "project"}-${createHash("sha256")
62
- .update(workingDir)
63
- .digest("hex")
64
- .slice(0, 12)}-onboarding.json`,
65
- );
40
+ const getOnboardingMarkerPath = async (workingDir: string): Promise<string> => {
41
+ const identity = await ensureAgentIdentity(workingDir);
42
+ return resolve(getAgentStoreDirectory(identity), "onboarding-state.json");
43
+ };
66
44
 
67
45
  const readMarker = async (
68
46
  workingDir: string,
69
47
  ): Promise<OnboardingMarkerState | undefined> => {
70
- const markerPath = getOnboardingMarkerPath(workingDir);
48
+ const markerPath = await getOnboardingMarkerPath(workingDir);
71
49
  try {
72
50
  await access(markerPath);
73
51
  } catch {
@@ -85,7 +63,7 @@ const writeMarker = async (
85
63
  workingDir: string,
86
64
  state: OnboardingMarkerState,
87
65
  ): Promise<void> => {
88
- const markerPath = getOnboardingMarkerPath(workingDir);
66
+ const markerPath = await getOnboardingMarkerPath(workingDir);
89
67
  await mkdir(dirname(markerPath), { recursive: true });
90
68
  await writeFile(markerPath, JSON.stringify(state, null, 2), "utf8");
91
69
  };