@poncho-ai/harness 0.24.0 → 0.25.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/.turbo/turbo-build.log +5 -5
- package/.turbo/turbo-lint.log +6 -0
- package/.turbo/turbo-test.log +135 -0
- package/CHANGELOG.md +6 -0
- package/dist/index.d.ts +17 -1
- package/dist/index.js +175 -78
- package/package.json +1 -1
- package/src/harness.ts +122 -38
- package/src/memory.ts +63 -46
- package/test/harness.test.ts +129 -2
- package/test/memory.test.ts +100 -15
package/dist/index.js
CHANGED
|
@@ -1402,7 +1402,8 @@ if (result.compacted) {
|
|
|
1402
1402
|
When \`memory.enabled\` is true in \`poncho.config.js\`, the harness enables a simple memory model:
|
|
1403
1403
|
|
|
1404
1404
|
- A single persistent main memory document is loaded at run start and interpolated into the system prompt under \`## Persistent Memory\`.
|
|
1405
|
-
- \`
|
|
1405
|
+
- \`memory_main_write\` overwrites the entire memory document (for initial writes or full rewrites).
|
|
1406
|
+
- \`memory_main_edit\` performs targeted string-replacement edits on memory (find \`old_str\`, replace with \`new_str\`), mirroring \`edit_file\` semantics. The tool description instructs the model to proactively evaluate each turn whether durable memory should be updated.
|
|
1406
1407
|
- \`conversation_recall\` can search recent prior conversations (keyword scoring) when historical context is relevant (\`as we discussed\`, \`last time\`, etc.).
|
|
1407
1408
|
|
|
1408
1409
|
\`\`\`javascript
|
|
@@ -1420,9 +1421,10 @@ export default {
|
|
|
1420
1421
|
|
|
1421
1422
|
Available memory tools:
|
|
1422
1423
|
|
|
1423
|
-
- \`memory_main_get\`
|
|
1424
|
-
- \`
|
|
1425
|
-
- \`
|
|
1424
|
+
- \`memory_main_get\` \u2014 read the current memory document
|
|
1425
|
+
- \`memory_main_write\` \u2014 overwrite the entire memory document
|
|
1426
|
+
- \`memory_main_edit\` \u2014 edit memory via exact string replacement (\`old_str\` / \`new_str\`)
|
|
1427
|
+
- \`conversation_recall\` \u2014 search past conversations
|
|
1426
1428
|
`,
|
|
1427
1429
|
"configuration": `# Configuration & Security
|
|
1428
1430
|
|
|
@@ -2028,6 +2030,8 @@ var ponchoDocsTool = defineTool({
|
|
|
2028
2030
|
|
|
2029
2031
|
// src/harness.ts
|
|
2030
2032
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
2033
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
2034
|
+
import { resolve as resolve9 } from "path";
|
|
2031
2035
|
import { getTextContent as getTextContent3 } from "@poncho-ai/sdk";
|
|
2032
2036
|
|
|
2033
2037
|
// src/upload-store.ts
|
|
@@ -2384,14 +2388,9 @@ var InMemoryMemoryStore = class {
|
|
|
2384
2388
|
return this.mainMemory;
|
|
2385
2389
|
}
|
|
2386
2390
|
async updateMainMemory(input) {
|
|
2387
|
-
const now2 = Date.now();
|
|
2388
|
-
const existing = await this.getMainMemory();
|
|
2389
|
-
const nextContent = input.mode === "append" && existing.content ? `${existing.content}
|
|
2390
|
-
|
|
2391
|
-
${input.content}`.trim() : input.content;
|
|
2392
2391
|
this.mainMemory = {
|
|
2393
|
-
content:
|
|
2394
|
-
updatedAt:
|
|
2392
|
+
content: input.content.trim(),
|
|
2393
|
+
updatedAt: Date.now()
|
|
2395
2394
|
};
|
|
2396
2395
|
return this.mainMemory;
|
|
2397
2396
|
}
|
|
@@ -2449,12 +2448,8 @@ var FileMainMemoryStore = class {
|
|
|
2449
2448
|
}
|
|
2450
2449
|
async updateMainMemory(input) {
|
|
2451
2450
|
await this.ensureLoaded();
|
|
2452
|
-
const existing = await this.getMainMemory();
|
|
2453
|
-
const nextContent = input.mode === "append" && existing.content ? `${existing.content}
|
|
2454
|
-
|
|
2455
|
-
${input.content}`.trim() : input.content;
|
|
2456
2451
|
this.mainMemory = {
|
|
2457
|
-
content:
|
|
2452
|
+
content: input.content.trim(),
|
|
2458
2453
|
updatedAt: Date.now()
|
|
2459
2454
|
};
|
|
2460
2455
|
await this.persist();
|
|
@@ -2493,8 +2488,7 @@ var KeyValueMainMemoryStoreBase = class {
|
|
|
2493
2488
|
}
|
|
2494
2489
|
} catch {
|
|
2495
2490
|
await this.memoryFallback.updateMainMemory({
|
|
2496
|
-
content: payload.main.content
|
|
2497
|
-
mode: "replace"
|
|
2491
|
+
content: payload.main.content
|
|
2498
2492
|
});
|
|
2499
2493
|
}
|
|
2500
2494
|
}
|
|
@@ -2505,11 +2499,8 @@ var KeyValueMainMemoryStoreBase = class {
|
|
|
2505
2499
|
async updateMainMemory(input) {
|
|
2506
2500
|
const key = this.key();
|
|
2507
2501
|
const payload = await this.readPayload(key);
|
|
2508
|
-
const nextContent = input.mode === "append" && payload.main.content ? `${payload.main.content}
|
|
2509
|
-
|
|
2510
|
-
${input.content}`.trim() : input.content;
|
|
2511
2502
|
payload.main = {
|
|
2512
|
-
content:
|
|
2503
|
+
content: input.content.trim(),
|
|
2513
2504
|
updatedAt: Date.now()
|
|
2514
2505
|
};
|
|
2515
2506
|
await this.writePayload(key, payload);
|
|
@@ -2775,19 +2766,14 @@ var createMemoryTools = (store, options) => {
|
|
|
2775
2766
|
}
|
|
2776
2767
|
}),
|
|
2777
2768
|
defineTool2({
|
|
2778
|
-
name: "
|
|
2779
|
-
description: "
|
|
2769
|
+
name: "memory_main_write",
|
|
2770
|
+
description: "Overwrite the entire persistent main memory document. Use for initial writes or full rewrites. Prefer memory_main_edit for targeted changes to existing memory.",
|
|
2780
2771
|
inputSchema: {
|
|
2781
2772
|
type: "object",
|
|
2782
2773
|
properties: {
|
|
2783
|
-
mode: {
|
|
2784
|
-
type: "string",
|
|
2785
|
-
enum: ["replace", "append"],
|
|
2786
|
-
description: "replace overwrites memory; append adds content to the end"
|
|
2787
|
-
},
|
|
2788
2774
|
content: {
|
|
2789
2775
|
type: "string",
|
|
2790
|
-
description: "The memory content to write"
|
|
2776
|
+
description: "The full memory content to write"
|
|
2791
2777
|
}
|
|
2792
2778
|
},
|
|
2793
2779
|
required: ["content"],
|
|
@@ -2798,8 +2784,50 @@ var createMemoryTools = (store, options) => {
|
|
|
2798
2784
|
if (!content) {
|
|
2799
2785
|
throw new Error("content is required");
|
|
2800
2786
|
}
|
|
2801
|
-
const
|
|
2802
|
-
|
|
2787
|
+
const memory = await store.updateMainMemory({ content });
|
|
2788
|
+
return { ok: true, memory };
|
|
2789
|
+
}
|
|
2790
|
+
}),
|
|
2791
|
+
defineTool2({
|
|
2792
|
+
name: "memory_main_edit",
|
|
2793
|
+
description: "Edit persistent main memory by replacing an exact string match with new content. The old_str must match exactly one location in memory. Use an empty new_str to delete matched content. Proactively evaluate every turn whether memory should be updated.",
|
|
2794
|
+
inputSchema: {
|
|
2795
|
+
type: "object",
|
|
2796
|
+
properties: {
|
|
2797
|
+
old_str: {
|
|
2798
|
+
type: "string",
|
|
2799
|
+
description: "The exact text to find and replace (must be unique in memory). Include surrounding context if needed to ensure uniqueness."
|
|
2800
|
+
},
|
|
2801
|
+
new_str: {
|
|
2802
|
+
type: "string",
|
|
2803
|
+
description: "The replacement text (use empty string to delete the matched content)"
|
|
2804
|
+
}
|
|
2805
|
+
},
|
|
2806
|
+
required: ["old_str", "new_str"],
|
|
2807
|
+
additionalProperties: false
|
|
2808
|
+
},
|
|
2809
|
+
handler: async (input) => {
|
|
2810
|
+
const oldStr = typeof input.old_str === "string" ? input.old_str : "";
|
|
2811
|
+
const newStr = typeof input.new_str === "string" ? input.new_str : "";
|
|
2812
|
+
if (!oldStr) {
|
|
2813
|
+
throw new Error("old_str must not be empty.");
|
|
2814
|
+
}
|
|
2815
|
+
const current = await store.getMainMemory();
|
|
2816
|
+
const content = current.content;
|
|
2817
|
+
const first = content.indexOf(oldStr);
|
|
2818
|
+
if (first === -1) {
|
|
2819
|
+
throw new Error(
|
|
2820
|
+
"old_str not found in memory. Make sure it matches exactly, including whitespace and line breaks."
|
|
2821
|
+
);
|
|
2822
|
+
}
|
|
2823
|
+
const last = content.lastIndexOf(oldStr);
|
|
2824
|
+
if (first !== last) {
|
|
2825
|
+
throw new Error(
|
|
2826
|
+
"old_str appears multiple times in memory. Please provide more context to ensure a unique match."
|
|
2827
|
+
);
|
|
2828
|
+
}
|
|
2829
|
+
const newContent = content.slice(0, first) + newStr + content.slice(first + oldStr.length);
|
|
2830
|
+
const memory = await store.updateMainMemory({ content: newContent });
|
|
2803
2831
|
return { ok: true, memory };
|
|
2804
2832
|
}
|
|
2805
2833
|
}),
|
|
@@ -4688,6 +4716,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
4688
4716
|
_browserSession;
|
|
4689
4717
|
_browserMod;
|
|
4690
4718
|
parsedAgent;
|
|
4719
|
+
agentFileFingerprint = "";
|
|
4691
4720
|
mcpBridge;
|
|
4692
4721
|
subagentManager;
|
|
4693
4722
|
resolveToolAccess(toolName) {
|
|
@@ -4800,20 +4829,17 @@ var AgentHarness = class _AgentHarness {
|
|
|
4800
4829
|
return this.parsedAgent?.frontmatter.approvalRequired?.scripts ?? [];
|
|
4801
4830
|
}
|
|
4802
4831
|
getRequestedMcpPatterns() {
|
|
4803
|
-
const
|
|
4832
|
+
const patterns = new Set(this.getAgentMcpIntent());
|
|
4804
4833
|
for (const skillName of this.activeSkillNames) {
|
|
4805
4834
|
const skill = this.loadedSkills.find((entry) => entry.name === skillName);
|
|
4806
4835
|
if (!skill) {
|
|
4807
4836
|
continue;
|
|
4808
4837
|
}
|
|
4809
4838
|
for (const pattern of skill.allowedTools.mcp) {
|
|
4810
|
-
|
|
4839
|
+
patterns.add(pattern);
|
|
4811
4840
|
}
|
|
4812
4841
|
}
|
|
4813
|
-
|
|
4814
|
-
return [...skillPatterns];
|
|
4815
|
-
}
|
|
4816
|
-
return this.getAgentMcpIntent();
|
|
4842
|
+
return [...patterns];
|
|
4817
4843
|
}
|
|
4818
4844
|
getRequestedScriptPatterns() {
|
|
4819
4845
|
const patterns = new Set(this.getAgentScriptIntent());
|
|
@@ -4829,20 +4855,17 @@ var AgentHarness = class _AgentHarness {
|
|
|
4829
4855
|
return [...patterns];
|
|
4830
4856
|
}
|
|
4831
4857
|
getRequestedMcpApprovalPatterns() {
|
|
4832
|
-
const
|
|
4858
|
+
const patterns = new Set(this.getAgentMcpApprovalPatterns());
|
|
4833
4859
|
for (const skillName of this.activeSkillNames) {
|
|
4834
4860
|
const skill = this.loadedSkills.find((entry) => entry.name === skillName);
|
|
4835
4861
|
if (!skill) {
|
|
4836
4862
|
continue;
|
|
4837
4863
|
}
|
|
4838
4864
|
for (const pattern of skill.approvalRequired.mcp) {
|
|
4839
|
-
|
|
4865
|
+
patterns.add(pattern);
|
|
4840
4866
|
}
|
|
4841
4867
|
}
|
|
4842
|
-
|
|
4843
|
-
return [...skillPatterns];
|
|
4844
|
-
}
|
|
4845
|
-
return this.getAgentMcpApprovalPatterns();
|
|
4868
|
+
return [...patterns];
|
|
4846
4869
|
}
|
|
4847
4870
|
getRequestedScriptApprovalPatterns() {
|
|
4848
4871
|
const patterns = new Set(this.getAgentScriptApprovalPatterns());
|
|
@@ -4973,13 +4996,54 @@ var AgentHarness = class _AgentHarness {
|
|
|
4973
4996
|
);
|
|
4974
4997
|
}
|
|
4975
4998
|
static SKILL_REFRESH_DEBOUNCE_MS = 3e3;
|
|
4976
|
-
|
|
4999
|
+
/**
|
|
5000
|
+
* Re-read AGENT.md and update the parsed agent when the file has changed
|
|
5001
|
+
* on disk. Returns `true` when the agent was actually re-parsed.
|
|
5002
|
+
*
|
|
5003
|
+
* Preserves the agent identity (id) across reloads so conversation
|
|
5004
|
+
* continuity isn't broken.
|
|
5005
|
+
*/
|
|
5006
|
+
async refreshAgentIfChanged() {
|
|
4977
5007
|
if (this.environment !== "development") {
|
|
4978
|
-
return;
|
|
5008
|
+
return false;
|
|
4979
5009
|
}
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
5010
|
+
try {
|
|
5011
|
+
const agentFilePath = resolve9(this.workingDir, "AGENT.md");
|
|
5012
|
+
const rawContent = await readFile7(agentFilePath, "utf8");
|
|
5013
|
+
if (rawContent === this.agentFileFingerprint) {
|
|
5014
|
+
return false;
|
|
5015
|
+
}
|
|
5016
|
+
const parsed = parseAgentMarkdown(rawContent);
|
|
5017
|
+
if (!parsed.frontmatter.id && this.parsedAgent?.frontmatter.id) {
|
|
5018
|
+
parsed.frontmatter.id = this.parsedAgent.frontmatter.id;
|
|
5019
|
+
}
|
|
5020
|
+
this.parsedAgent = parsed;
|
|
5021
|
+
this.agentFileFingerprint = rawContent;
|
|
5022
|
+
return true;
|
|
5023
|
+
} catch (error) {
|
|
5024
|
+
console.warn(
|
|
5025
|
+
`[poncho][agent] Failed to refresh AGENT.md in development mode: ${error instanceof Error ? error.message : String(error)}`
|
|
5026
|
+
);
|
|
5027
|
+
return false;
|
|
5028
|
+
}
|
|
5029
|
+
}
|
|
5030
|
+
/**
|
|
5031
|
+
* Re-scan skill directories and update metadata, tools, and context window
|
|
5032
|
+
* when skills have changed on disk. Returns `true` when the skill set was
|
|
5033
|
+
* actually updated.
|
|
5034
|
+
*
|
|
5035
|
+
* @param force - bypass the time-based debounce (used for mid-run refreshes
|
|
5036
|
+
* after the agent may have written new skill files).
|
|
5037
|
+
*/
|
|
5038
|
+
async refreshSkillsIfChanged(force = false) {
|
|
5039
|
+
if (this.environment !== "development") {
|
|
5040
|
+
return false;
|
|
5041
|
+
}
|
|
5042
|
+
if (!force) {
|
|
5043
|
+
const elapsed = Date.now() - this.lastSkillRefreshAt;
|
|
5044
|
+
if (this.lastSkillRefreshAt > 0 && elapsed < _AgentHarness.SKILL_REFRESH_DEBOUNCE_MS) {
|
|
5045
|
+
return false;
|
|
5046
|
+
}
|
|
4983
5047
|
}
|
|
4984
5048
|
this.lastSkillRefreshAt = Date.now();
|
|
4985
5049
|
try {
|
|
@@ -4989,22 +5053,35 @@ var AgentHarness = class _AgentHarness {
|
|
|
4989
5053
|
);
|
|
4990
5054
|
const nextFingerprint = this.buildSkillFingerprint(latestSkills);
|
|
4991
5055
|
if (nextFingerprint === this.skillFingerprint) {
|
|
4992
|
-
return;
|
|
5056
|
+
return false;
|
|
4993
5057
|
}
|
|
4994
5058
|
this.loadedSkills = latestSkills;
|
|
4995
5059
|
this.skillContextWindow = buildSkillContextWindow(latestSkills);
|
|
4996
5060
|
this.skillFingerprint = nextFingerprint;
|
|
4997
5061
|
this.registerSkillTools(latestSkills);
|
|
4998
|
-
|
|
5062
|
+
const latestSkillNames = new Set(latestSkills.map((s) => s.name));
|
|
5063
|
+
for (const name of this.activeSkillNames) {
|
|
5064
|
+
if (!latestSkillNames.has(name)) {
|
|
5065
|
+
this.activeSkillNames.delete(name);
|
|
5066
|
+
}
|
|
5067
|
+
}
|
|
5068
|
+
if (this.mcpBridge) {
|
|
5069
|
+
await this.mcpBridge.discoverTools();
|
|
5070
|
+
}
|
|
4999
5071
|
await this.refreshMcpTools("skills:changed");
|
|
5072
|
+
return true;
|
|
5000
5073
|
} catch (error) {
|
|
5001
5074
|
console.warn(
|
|
5002
5075
|
`[poncho][skills] Failed to refresh skills in development mode: ${error instanceof Error ? error.message : String(error)}`
|
|
5003
5076
|
);
|
|
5077
|
+
return false;
|
|
5004
5078
|
}
|
|
5005
5079
|
}
|
|
5006
5080
|
async initialize() {
|
|
5007
|
-
|
|
5081
|
+
const agentFilePath = resolve9(this.workingDir, "AGENT.md");
|
|
5082
|
+
const agentRawContent = await readFile7(agentFilePath, "utf8");
|
|
5083
|
+
this.parsedAgent = parseAgentMarkdown(agentRawContent);
|
|
5084
|
+
this.agentFileFingerprint = agentRawContent;
|
|
5008
5085
|
const identity = await ensureAgentIdentity(this.workingDir);
|
|
5009
5086
|
if (!this.parsedAgent.frontmatter.id) {
|
|
5010
5087
|
this.parsedAgent.frontmatter.id = identity.id;
|
|
@@ -5082,9 +5159,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
5082
5159
|
await writeFile6(filePath, json, "utf8");
|
|
5083
5160
|
},
|
|
5084
5161
|
async load() {
|
|
5085
|
-
const { readFile:
|
|
5162
|
+
const { readFile: readFile9 } = await import("fs/promises");
|
|
5086
5163
|
try {
|
|
5087
|
-
return await
|
|
5164
|
+
return await readFile9(filePath, "utf8");
|
|
5088
5165
|
} catch {
|
|
5089
5166
|
return void 0;
|
|
5090
5167
|
}
|
|
@@ -5248,9 +5325,9 @@ var AgentHarness = class _AgentHarness {
|
|
|
5248
5325
|
for await (const event of this.run(input)) {
|
|
5249
5326
|
eventQueue.push(event);
|
|
5250
5327
|
if (queueResolve) {
|
|
5251
|
-
const
|
|
5328
|
+
const resolve11 = queueResolve;
|
|
5252
5329
|
queueResolve = null;
|
|
5253
|
-
|
|
5330
|
+
resolve11();
|
|
5254
5331
|
}
|
|
5255
5332
|
}
|
|
5256
5333
|
} catch (error) {
|
|
@@ -5269,8 +5346,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
5269
5346
|
if (eventQueue.length > 0) {
|
|
5270
5347
|
yield eventQueue.shift();
|
|
5271
5348
|
} else if (!generatorDone) {
|
|
5272
|
-
await new Promise((
|
|
5273
|
-
queueResolve =
|
|
5349
|
+
await new Promise((resolve11) => {
|
|
5350
|
+
queueResolve = resolve11;
|
|
5274
5351
|
});
|
|
5275
5352
|
}
|
|
5276
5353
|
}
|
|
@@ -5308,13 +5385,14 @@ var AgentHarness = class _AgentHarness {
|
|
|
5308
5385
|
await this.initialize();
|
|
5309
5386
|
}
|
|
5310
5387
|
const memoryPromise = this.memoryStore ? this.memoryStore.getMainMemory() : void 0;
|
|
5388
|
+
await this.refreshAgentIfChanged();
|
|
5311
5389
|
await this.refreshSkillsIfChanged();
|
|
5312
5390
|
this._currentRunConversationId = input.conversationId;
|
|
5313
5391
|
const ownerParam = input.parameters?.__ownerId;
|
|
5314
5392
|
if (typeof ownerParam === "string") {
|
|
5315
5393
|
this._currentRunOwnerId = ownerParam;
|
|
5316
5394
|
}
|
|
5317
|
-
|
|
5395
|
+
let agent = this.parsedAgent;
|
|
5318
5396
|
const runId = `run_${randomUUID3()}`;
|
|
5319
5397
|
const start = now();
|
|
5320
5398
|
const maxSteps = agent.frontmatter.limits?.maxSteps ?? 50;
|
|
@@ -5325,11 +5403,11 @@ var AgentHarness = class _AgentHarness {
|
|
|
5325
5403
|
const messages = [...input.messages ?? []];
|
|
5326
5404
|
const inputMessageCount = messages.length;
|
|
5327
5405
|
const events = [];
|
|
5328
|
-
const
|
|
5406
|
+
const renderCurrentAgentPrompt = () => renderAgentPrompt(this.parsedAgent, {
|
|
5329
5407
|
parameters: input.parameters,
|
|
5330
5408
|
runtime: {
|
|
5331
5409
|
runId,
|
|
5332
|
-
agentId:
|
|
5410
|
+
agentId: this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name,
|
|
5333
5411
|
environment: this.environment,
|
|
5334
5412
|
workingDir: this.workingDir
|
|
5335
5413
|
}
|
|
@@ -5357,9 +5435,6 @@ Browser sessions (cookies, localStorage, login state) are automatically saved an
|
|
|
5357
5435
|
|
|
5358
5436
|
### Tabs and resources
|
|
5359
5437
|
Each conversation gets its own browser tab sharing a single browser instance. Call \`browser_close\` when done to free the tab. If you don't close it, the tab stays open and the user can continue interacting with it.` : "";
|
|
5360
|
-
const promptWithSkills = this.skillContextWindow ? `${systemPrompt}${developmentContext}
|
|
5361
|
-
|
|
5362
|
-
${this.skillContextWindow}${browserContext}` : `${systemPrompt}${developmentContext}${browserContext}`;
|
|
5363
5438
|
const mainMemory = await memoryPromise;
|
|
5364
5439
|
const boundedMainMemory = mainMemory && mainMemory.content.length > 4e3 ? `${mainMemory.content.slice(0, 4e3)}
|
|
5365
5440
|
...[truncated]` : mainMemory?.content;
|
|
@@ -5367,7 +5442,12 @@ ${this.skillContextWindow}${browserContext}` : `${systemPrompt}${developmentCont
|
|
|
5367
5442
|
## Persistent Memory
|
|
5368
5443
|
|
|
5369
5444
|
${boundedMainMemory.trim()}` : "";
|
|
5370
|
-
const
|
|
5445
|
+
const buildSystemPrompt = () => {
|
|
5446
|
+
const agentPrompt = renderCurrentAgentPrompt();
|
|
5447
|
+
const promptWithSkills = this.skillContextWindow ? `${agentPrompt}${developmentContext}
|
|
5448
|
+
|
|
5449
|
+
${this.skillContextWindow}${browserContext}` : `${agentPrompt}${developmentContext}${browserContext}`;
|
|
5450
|
+
return `${promptWithSkills}${memoryContext}
|
|
5371
5451
|
|
|
5372
5452
|
## Execution Integrity
|
|
5373
5453
|
|
|
@@ -5375,6 +5455,10 @@ ${boundedMainMemory.trim()}` : "";
|
|
|
5375
5455
|
- Do not fabricate "Tool Used" or "Tool Result" logs as plain text.
|
|
5376
5456
|
- Never output faux execution transcripts, markdown tool logs, or "Tool Used/Result" sections.
|
|
5377
5457
|
- If no suitable tool is available, explicitly say that and ask for guidance.`;
|
|
5458
|
+
};
|
|
5459
|
+
let integrityPrompt = buildSystemPrompt();
|
|
5460
|
+
let lastPromptFingerprint = `${this.agentFileFingerprint}
|
|
5461
|
+
${this.skillFingerprint}`;
|
|
5378
5462
|
const pushEvent = (event) => {
|
|
5379
5463
|
events.push(event);
|
|
5380
5464
|
return event;
|
|
@@ -5797,8 +5881,8 @@ ${textContent}` };
|
|
|
5797
5881
|
let timer;
|
|
5798
5882
|
nextPart = await Promise.race([
|
|
5799
5883
|
fullStreamIterator.next(),
|
|
5800
|
-
new Promise((
|
|
5801
|
-
timer = setTimeout(() =>
|
|
5884
|
+
new Promise((resolve11) => {
|
|
5885
|
+
timer = setTimeout(() => resolve11(null), timeout);
|
|
5802
5886
|
})
|
|
5803
5887
|
]);
|
|
5804
5888
|
clearTimeout(timer);
|
|
@@ -6106,6 +6190,19 @@ ${textContent}` };
|
|
|
6106
6190
|
content: JSON.stringify(toolResultsForModel),
|
|
6107
6191
|
metadata: toolMsgMeta
|
|
6108
6192
|
});
|
|
6193
|
+
if (this.environment === "development") {
|
|
6194
|
+
const agentChanged = await this.refreshAgentIfChanged();
|
|
6195
|
+
const skillsChanged = await this.refreshSkillsIfChanged(true);
|
|
6196
|
+
if (agentChanged || skillsChanged) {
|
|
6197
|
+
agent = this.parsedAgent;
|
|
6198
|
+
const currentFingerprint = `${this.agentFileFingerprint}
|
|
6199
|
+
${this.skillFingerprint}`;
|
|
6200
|
+
if (currentFingerprint !== lastPromptFingerprint) {
|
|
6201
|
+
integrityPrompt = buildSystemPrompt();
|
|
6202
|
+
lastPromptFingerprint = currentFingerprint;
|
|
6203
|
+
}
|
|
6204
|
+
}
|
|
6205
|
+
}
|
|
6109
6206
|
yield pushEvent({
|
|
6110
6207
|
type: "step:completed",
|
|
6111
6208
|
step,
|
|
@@ -6294,8 +6391,8 @@ var LatitudeCapture = class {
|
|
|
6294
6391
|
|
|
6295
6392
|
// src/state.ts
|
|
6296
6393
|
import { randomUUID as randomUUID4 } from "crypto";
|
|
6297
|
-
import { mkdir as mkdir4, readFile as
|
|
6298
|
-
import { dirname as dirname4, resolve as
|
|
6394
|
+
import { mkdir as mkdir4, readFile as readFile8, readdir as readdir4, rename as rename2, rm as rm3, writeFile as writeFile5 } from "fs/promises";
|
|
6395
|
+
import { dirname as dirname4, resolve as resolve10 } from "path";
|
|
6299
6396
|
var DEFAULT_OWNER = "local-owner";
|
|
6300
6397
|
var LOCAL_STATE_FILE = "state.json";
|
|
6301
6398
|
var CONVERSATIONS_DIRECTORY = "conversations";
|
|
@@ -6488,8 +6585,8 @@ var FileConversationStore = class {
|
|
|
6488
6585
|
agentId: this.agentId
|
|
6489
6586
|
});
|
|
6490
6587
|
const agentDir = getAgentStoreDirectory(identity);
|
|
6491
|
-
const conversationsDir =
|
|
6492
|
-
const indexPath =
|
|
6588
|
+
const conversationsDir = resolve10(agentDir, CONVERSATIONS_DIRECTORY);
|
|
6589
|
+
const indexPath = resolve10(conversationsDir, LOCAL_CONVERSATION_INDEX_FILE);
|
|
6493
6590
|
this.paths = { conversationsDir, indexPath };
|
|
6494
6591
|
return this.paths;
|
|
6495
6592
|
}
|
|
@@ -6503,9 +6600,9 @@ var FileConversationStore = class {
|
|
|
6503
6600
|
}
|
|
6504
6601
|
async readConversationFile(fileName) {
|
|
6505
6602
|
const { conversationsDir } = await this.resolvePaths();
|
|
6506
|
-
const filePath =
|
|
6603
|
+
const filePath = resolve10(conversationsDir, fileName);
|
|
6507
6604
|
try {
|
|
6508
|
-
const raw = await
|
|
6605
|
+
const raw = await readFile8(filePath, "utf8");
|
|
6509
6606
|
return JSON.parse(raw);
|
|
6510
6607
|
} catch {
|
|
6511
6608
|
return void 0;
|
|
@@ -6551,7 +6648,7 @@ var FileConversationStore = class {
|
|
|
6551
6648
|
this.loaded = true;
|
|
6552
6649
|
const { indexPath } = await this.resolvePaths();
|
|
6553
6650
|
try {
|
|
6554
|
-
const raw = await
|
|
6651
|
+
const raw = await readFile8(indexPath, "utf8");
|
|
6555
6652
|
const parsed = JSON.parse(raw);
|
|
6556
6653
|
for (const conversation of parsed.conversations ?? []) {
|
|
6557
6654
|
this.conversations.set(conversation.conversationId, conversation);
|
|
@@ -6574,7 +6671,7 @@ var FileConversationStore = class {
|
|
|
6574
6671
|
const { conversationsDir } = await this.resolvePaths();
|
|
6575
6672
|
const existing = this.conversations.get(conversation.conversationId);
|
|
6576
6673
|
const fileName = existing?.fileName ?? this.resolveConversationFileName(conversation);
|
|
6577
|
-
const filePath =
|
|
6674
|
+
const filePath = resolve10(conversationsDir, fileName);
|
|
6578
6675
|
this.writing = this.writing.then(async () => {
|
|
6579
6676
|
await writeJsonAtomic2(filePath, conversation);
|
|
6580
6677
|
this.conversations.set(conversation.conversationId, {
|
|
@@ -6672,7 +6769,7 @@ var FileConversationStore = class {
|
|
|
6672
6769
|
if (removed) {
|
|
6673
6770
|
this.writing = this.writing.then(async () => {
|
|
6674
6771
|
if (existing) {
|
|
6675
|
-
await rm3(
|
|
6772
|
+
await rm3(resolve10(conversationsDir, existing.fileName), { force: true });
|
|
6676
6773
|
}
|
|
6677
6774
|
await this.writeIndex();
|
|
6678
6775
|
});
|
|
@@ -6702,7 +6799,7 @@ var FileStateStore = class {
|
|
|
6702
6799
|
workingDir: this.workingDir,
|
|
6703
6800
|
agentId: this.agentId
|
|
6704
6801
|
});
|
|
6705
|
-
this.filePath =
|
|
6802
|
+
this.filePath = resolve10(getAgentStoreDirectory(identity), LOCAL_STATE_FILE);
|
|
6706
6803
|
}
|
|
6707
6804
|
isExpired(state) {
|
|
6708
6805
|
return typeof this.ttlMs === "number" && Date.now() - state.updatedAt > this.ttlMs;
|
|
@@ -6714,7 +6811,7 @@ var FileStateStore = class {
|
|
|
6714
6811
|
}
|
|
6715
6812
|
this.loaded = true;
|
|
6716
6813
|
try {
|
|
6717
|
-
const raw = await
|
|
6814
|
+
const raw = await readFile8(this.filePath, "utf8");
|
|
6718
6815
|
const parsed = JSON.parse(raw);
|
|
6719
6816
|
for (const state of parsed.states ?? []) {
|
|
6720
6817
|
this.states.set(state.runId, state);
|