@poncho-ai/harness 0.47.1 → 0.49.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/CHANGELOG.md +43 -0
- package/dist/index.d.ts +34 -5
- package/dist/index.js +24 -9
- package/package.json +1 -1
- package/src/config.ts +4 -0
- package/src/harness.ts +46 -8
- package/src/memory.ts +9 -0
- package/src/skill-context.ts +11 -1
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @poncho-ai/harness@0.
|
|
2
|
+
> @poncho-ai/harness@0.49.0 build /home/runner/work/poncho-ai/poncho-ai/packages/harness
|
|
3
3
|
> node scripts/embed-docs.js && tsup src/index.ts --format esm --dts
|
|
4
4
|
|
|
5
5
|
[embed-docs] Generated poncho-docs.ts with 4 topics
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
[34mCLI[39m tsup v8.5.1
|
|
9
9
|
[34mCLI[39m Target: es2022
|
|
10
10
|
[34mESM[39m Build start
|
|
11
|
-
[32mESM[39m [1mdist/index.js [22m[32m527.60 KB[39m
|
|
12
11
|
[32mESM[39m [1mdist/isolate-VY35DGLM.js [22m[32m49.43 KB[39m
|
|
13
|
-
[32mESM[39m
|
|
12
|
+
[32mESM[39m [1mdist/index.js [22m[32m528.58 KB[39m
|
|
13
|
+
[32mESM[39m ⚡️ Build success in 197ms
|
|
14
14
|
[34mDTS[39m Build start
|
|
15
|
-
[32mDTS[39m ⚡️ Build success in
|
|
16
|
-
[32mDTS[39m [1mdist/index.d.ts [22m[
|
|
15
|
+
[32mDTS[39m ⚡️ Build success in 5685ms
|
|
16
|
+
[32mDTS[39m [1mdist/index.d.ts [22m[32m87.88 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# @poncho-ai/harness
|
|
2
2
|
|
|
3
|
+
## 0.49.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#127](https://github.com/cesr/poncho-ai/pull/127) [`87b40d9`](https://github.com/cesr/poncho-ai/commit/87b40d9d6cebba4ac646598d154a767a1d2f3551) Thanks [@cesr](https://github.com/cesr)! - harness: stop truncating main memory by default
|
|
8
|
+
|
|
9
|
+
Main memory injected into the system prompt was hard-truncated at 4000
|
|
10
|
+
characters with a `...[truncated]` marker. Silently dropping the tail of
|
|
11
|
+
a user's memory every turn is a footgun, so the **default is now no
|
|
12
|
+
truncation** — the full memory is injected.
|
|
13
|
+
|
|
14
|
+
New `MemoryConfig.maxPromptChars` (also settable via
|
|
15
|
+
`storage.memory.maxPromptChars`) lets a consumer opt back _into_ a cap
|
|
16
|
+
for prompt-cost control: set a positive number and content beyond it is
|
|
17
|
+
sliced with the `...[truncated]` marker as before.
|
|
18
|
+
|
|
19
|
+
Behavior change: consumers that relied on the implicit 4000-char cap
|
|
20
|
+
will now see full memory in the prompt. To restore the old behavior set
|
|
21
|
+
`maxPromptChars: 4000`.
|
|
22
|
+
|
|
23
|
+
## 0.48.0
|
|
24
|
+
|
|
25
|
+
### Minor Changes
|
|
26
|
+
|
|
27
|
+
- [#125](https://github.com/cesr/poncho-ai/pull/125) [`ff66aae`](https://github.com/cesr/poncho-ai/commit/ff66aaeebe6017ca9e1ee4b31ffe0d89bdf5ef28) Thanks [@cesr](https://github.com/cesr)! - harness: add `systemSkillPaths` for platform-shipped system skills
|
|
28
|
+
|
|
29
|
+
New optional `HarnessOptions.systemSkillPaths` (absolute directories,
|
|
30
|
+
each scanned for `<name>/SKILL.md` at init). System skills are surfaced
|
|
31
|
+
in `<available_skills>` like any other skill, with their bodies read
|
|
32
|
+
from local disk on activation — letting a platform ship default skills
|
|
33
|
+
with the deploy instead of writing them into every tenant's VFS.
|
|
34
|
+
|
|
35
|
+
Precedence is purely additive: per tenant the skill set resolves as
|
|
36
|
+
repo skills > the tenant's own VFS skills > system skills. So a tenant's
|
|
37
|
+
`/skills/<same-name>/` overrides a same-named system skill (mirroring
|
|
38
|
+
the VFS override behavior platforms already rely on for system jobs),
|
|
39
|
+
and the existing repo-vs-VFS precedence is unchanged. Empty by default —
|
|
40
|
+
no behavior change for existing consumers.
|
|
41
|
+
|
|
42
|
+
Also exports `loadSkillMetadataFromDirs(dirs)` (extracted from
|
|
43
|
+
`loadSkillMetadata`) for scanning an explicit list of absolute skill
|
|
44
|
+
directories.
|
|
45
|
+
|
|
3
46
|
## 0.47.1
|
|
4
47
|
|
|
5
48
|
### Patch Changes
|
package/dist/index.d.ts
CHANGED
|
@@ -341,6 +341,15 @@ interface MemoryConfig {
|
|
|
341
341
|
region?: string;
|
|
342
342
|
ttl?: number;
|
|
343
343
|
maxRecallConversations?: number;
|
|
344
|
+
/**
|
|
345
|
+
* Optional cap on the characters of main memory injected into the
|
|
346
|
+
* system prompt each turn. Default is **no cap** — the full memory is
|
|
347
|
+
* injected (silently truncating a user's memory every turn is a
|
|
348
|
+
* footgun). Set a positive number to opt into truncation for
|
|
349
|
+
* prompt-cost control; content beyond it is sliced with a
|
|
350
|
+
* `...[truncated]` marker.
|
|
351
|
+
*/
|
|
352
|
+
maxPromptChars?: number;
|
|
344
353
|
}
|
|
345
354
|
interface MemoryStore {
|
|
346
355
|
getMainMemory(): Promise<MainMemory>;
|
|
@@ -445,6 +454,7 @@ interface StorageConfig {
|
|
|
445
454
|
memory?: {
|
|
446
455
|
enabled?: boolean;
|
|
447
456
|
maxRecallConversations?: number;
|
|
457
|
+
maxPromptChars?: number;
|
|
448
458
|
};
|
|
449
459
|
limits?: {
|
|
450
460
|
maxFileSize?: number;
|
|
@@ -1211,6 +1221,17 @@ interface HarnessOptions {
|
|
|
1211
1221
|
* Empty by default — no system mounts in the CLI / dev workflow.
|
|
1212
1222
|
*/
|
|
1213
1223
|
virtualMounts?: VirtualMount[];
|
|
1224
|
+
/**
|
|
1225
|
+
* Absolute directories of platform-shipped "system" skills. Each is
|
|
1226
|
+
* scanned for `<name>/SKILL.md` at init; the bodies live on local disk
|
|
1227
|
+
* and ship with the deploy. System skills are surfaced in
|
|
1228
|
+
* `<available_skills>` like any other skill, but sit at the LOWEST
|
|
1229
|
+
* precedence: a tenant's own `/skills/<same-name>/` (and a repo skill)
|
|
1230
|
+
* overrides a system skill of the same name. Pair with a read-only
|
|
1231
|
+
* `virtualMounts` entry (e.g. "/system/skills/") if the same files
|
|
1232
|
+
* should also be browsable in the VFS. Empty by default.
|
|
1233
|
+
*/
|
|
1234
|
+
systemSkillPaths?: string[];
|
|
1214
1235
|
}
|
|
1215
1236
|
interface HarnessRunOutput {
|
|
1216
1237
|
runId: string;
|
|
@@ -1243,6 +1264,8 @@ declare class AgentHarness {
|
|
|
1243
1264
|
private loadedConfig?;
|
|
1244
1265
|
private readonly injectedConfig?;
|
|
1245
1266
|
private loadedSkills;
|
|
1267
|
+
private systemSkills;
|
|
1268
|
+
private readonly systemSkillPaths;
|
|
1246
1269
|
private skillFingerprint;
|
|
1247
1270
|
private lastSkillRefreshAt;
|
|
1248
1271
|
private readonly activeSkillNames;
|
|
@@ -1298,10 +1321,15 @@ declare class AgentHarness {
|
|
|
1298
1321
|
private getMemoryStore;
|
|
1299
1322
|
private listActiveSkills;
|
|
1300
1323
|
/**
|
|
1301
|
-
* Resolve the skill set visible to a given tenant
|
|
1302
|
-
*
|
|
1303
|
-
*
|
|
1304
|
-
*
|
|
1324
|
+
* Resolve the skill set visible to a given tenant. Three tiers, by
|
|
1325
|
+
* precedence: repo skills > the tenant's own VFS skills > platform
|
|
1326
|
+
* system skills. So a repo skill wins over a same-named VFS skill
|
|
1327
|
+
* (unchanged), and a tenant's `/skills/<name>/` overrides a same-named
|
|
1328
|
+
* system skill (the deploy-shipped default). Cached per tenant; cache
|
|
1329
|
+
* invalidates on VFS writes under /skills/ via invalidateSkillsForTenant.
|
|
1330
|
+
* System skills are static within a process, so they don't participate
|
|
1331
|
+
* in the fingerprint — but a VFS override does (it changes a /skills
|
|
1332
|
+
* path), which recomputes the cache and lets the override take effect.
|
|
1305
1333
|
*/
|
|
1306
1334
|
private getSkillsForTenant;
|
|
1307
1335
|
invalidateSkillsForTenant(tenantId: string): void;
|
|
@@ -1451,6 +1479,7 @@ declare const parseSkillFrontmatter: (content: string) => {
|
|
|
1451
1479
|
};
|
|
1452
1480
|
} | undefined;
|
|
1453
1481
|
declare const loadSkillMetadata: (workingDir: string, extraSkillPaths?: string[]) => Promise<SkillMetadata[]>;
|
|
1482
|
+
declare const loadSkillMetadataFromDirs: (skillDirs: string[]) => Promise<SkillMetadata[]>;
|
|
1454
1483
|
declare const buildSkillContextWindow: (skills: SkillMetadata[]) => string;
|
|
1455
1484
|
declare const loadVfsSkillMetadata: (engine: StorageEngine, tenantId: string) => Promise<SkillMetadata[]>;
|
|
1456
1485
|
declare const mergeSkills: (repoSkills: SkillMetadata[], vfsSkills: SkillMetadata[], onCollision?: (vfsSkill: SkillMetadata) => void) => SkillMetadata[];
|
|
@@ -2080,4 +2109,4 @@ interface RunConversationTurnResult {
|
|
|
2080
2109
|
}
|
|
2081
2110
|
declare const runConversationTurn: (opts: RunConversationTurnOpts) => Promise<RunConversationTurnResult>;
|
|
2082
2111
|
|
|
2083
|
-
export { type ActiveConversationRun, type ActiveSubagentRun, type AgentFrontmatter, AgentHarness, type AgentIdentity, type AgentLimitsConfig, type AgentModelConfig, AgentOrchestrator, type ApprovalEventItem, type ArchivedToolResult$1 as ArchivedToolResult, type BashConfig, BashEnvironmentManager, type BashExecutionLimits, type BuiltInToolToggles, CALLBACK_LOCK_STALE_MS, type CompactMessagesOptions, type CompactResult, type CompactionConfig, type ContinuationHooks, type Conversation, type ConversationCreateInit, type ConversationState, type ConversationStatusSnapshot, type ConversationStore, type ConversationSummary, type CreateSkillToolsOptions, type CronJobConfig, DEFAULT_AGENT_DESCRIPTION, DEFAULT_AGENT_NAME, DEFAULT_MAX_STEPS, DEFAULT_MODEL_NAME, DEFAULT_MODEL_PROVIDER, DEFAULT_TEMPERATURE, DEFAULT_TIMEOUT, type DefaultAgentDefinitionOptions, type EventSink, type ExecuteTurnResult, type HarnessOptions, type HarnessRunOutput, type HistorySource, InMemoryConversationStore, InMemoryEngine, InMemoryStateStore, type IsolateBinding, type IsolateConfig, LocalMcpBridge, LocalUploadStore, MAX_CONCURRENT_SUBAGENTS, MAX_CONTINUATION_COUNT, MAX_SUBAGENT_CALLBACK_COUNT, MAX_SUBAGENT_NESTING, type MainMemory, type McpConfig, type MemoryConfig, type MemoryStore, type MessagingChannelConfig, type ModelProviderFactory, type NetworkConfig, OPENAI_CODEX_CLIENT_ID, type OpenAICodexAuthConfig, type OpenAICodexDeviceAuthRequest, type OpenAICodexSession, type OrchestratorHooks, type OrchestratorOptions, type OtlpConfig, type OtlpOption, PONCHO_UPLOAD_SCHEME, type ParsedAgent, type PendingSubagentApproval, type PendingSubagentResult, type PendingToolCall, type PonchoConfig, PonchoFsAdapter, PostgresEngine, type ProviderConfig, type Recurrence, type RecurrenceType, type Reminder, type ReminderCreateInput, type ReminderStatus, type ReminderStore, type RemoteMcpServerConfig, type RunConversationTurnOpts, type RunConversationTurnResult, type RunOutcome, type RunRequest, type RuntimeRenderContext, S3UploadStore, STALE_SUBAGENT_THRESHOLD_MS, STORAGE_SCHEMA_VERSION, type SecretsStore, type SkillContextEntry, type SkillMetadata, type SkillSource, SqliteEngine, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type StorageEngine, type StorageFactoryOptions, type StorageProvider, type StoredApproval, type SubagentManager, type SubagentResult, type SubagentSpawnResult, type SubagentSummary, type SubagentTranscript, type SubagentTranscriptMode, TOOL_RESULT_ARCHIVE_PARAM, type TelemetryConfig, TelemetryEmitter, type TenantTokenPayload, type ToolAccess, type ToolCall, ToolDispatcher, type ToolExecutionResult, type TurnDraftState, type TurnResultMetadata, type TurnSection, type UploadStore, type UploadsConfig, VFS_SCHEME, VercelBlobUploadStore, type VfsDirEntry, type VfsStat, type VirtualMount, applyTurnMetadata, buildAgentDirectoryName, buildApprovalCheckpoints, buildAssistantMetadata, buildSkillContextWindow, buildToolCompletedText, cloneSections, compactMessages, completeOpenAICodexDeviceAuth, computeNextOccurrence, createBashTool, createConversationStore, createConversationStoreFromEngine, createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createMemoryStore, createMemoryStoreFromEngine, createMemoryTools, createModelProvider, createReminderStore, createReminderStoreFromEngine, createReminderTools, createSearchTools, createSecretsStore, createSkillTools, createStateStore, createStorageEngine, createSubagentTools, createTodoStoreFromEngine, createTurnDraftState, createUploadStore, createWriteTool, decodeFileInputData, defaultAgentDefinition, deleteOpenAICodexSession, deriveUploadKey, ensureAgentIdentity, estimateTokens, estimateTotalTokens, executeConversationTurn, findSafeSplitPoint, flushTurnDraft, generateAgentId, getAgentStoreDirectory, getModelContextWindow, getOpenAICodexAccessToken, getOpenAICodexAuthFilePath, getOpenAICodexRequiredScopes, getPonchoStoreRoot, isMessageArray, jsonSchemaToZod, loadCanonicalHistory, loadPonchoConfig, loadRunHistory, loadSkillContext, loadSkillInstructions, loadSkillMetadata, loadVfsSkillMetadata, mergeSkills, normalizeApprovalCheckpoint, normalizeOtlp, normalizeScriptPolicyPath, normalizeToolAccess, parseAgentFile, parseAgentMarkdown, parseSkillFrontmatter, ponchoDocsTool, readOpenAICodexSession, readSkillResource, recordStandardTurnEvent, renderAgentPrompt, resolveAgentIdentity, resolveCompactionConfig, resolveEnv, resolveMemoryConfig, resolveRunRequest, resolveSkillDirs, resolveStateConfig, runConversationTurn, slugifyStorageComponent, startOpenAICodexDeviceAuth, verifyTenantToken, withToolResultArchiveParam, writeOpenAICodexSession };
|
|
2112
|
+
export { type ActiveConversationRun, type ActiveSubagentRun, type AgentFrontmatter, AgentHarness, type AgentIdentity, type AgentLimitsConfig, type AgentModelConfig, AgentOrchestrator, type ApprovalEventItem, type ArchivedToolResult$1 as ArchivedToolResult, type BashConfig, BashEnvironmentManager, type BashExecutionLimits, type BuiltInToolToggles, CALLBACK_LOCK_STALE_MS, type CompactMessagesOptions, type CompactResult, type CompactionConfig, type ContinuationHooks, type Conversation, type ConversationCreateInit, type ConversationState, type ConversationStatusSnapshot, type ConversationStore, type ConversationSummary, type CreateSkillToolsOptions, type CronJobConfig, DEFAULT_AGENT_DESCRIPTION, DEFAULT_AGENT_NAME, DEFAULT_MAX_STEPS, DEFAULT_MODEL_NAME, DEFAULT_MODEL_PROVIDER, DEFAULT_TEMPERATURE, DEFAULT_TIMEOUT, type DefaultAgentDefinitionOptions, type EventSink, type ExecuteTurnResult, type HarnessOptions, type HarnessRunOutput, type HistorySource, InMemoryConversationStore, InMemoryEngine, InMemoryStateStore, type IsolateBinding, type IsolateConfig, LocalMcpBridge, LocalUploadStore, MAX_CONCURRENT_SUBAGENTS, MAX_CONTINUATION_COUNT, MAX_SUBAGENT_CALLBACK_COUNT, MAX_SUBAGENT_NESTING, type MainMemory, type McpConfig, type MemoryConfig, type MemoryStore, type MessagingChannelConfig, type ModelProviderFactory, type NetworkConfig, OPENAI_CODEX_CLIENT_ID, type OpenAICodexAuthConfig, type OpenAICodexDeviceAuthRequest, type OpenAICodexSession, type OrchestratorHooks, type OrchestratorOptions, type OtlpConfig, type OtlpOption, PONCHO_UPLOAD_SCHEME, type ParsedAgent, type PendingSubagentApproval, type PendingSubagentResult, type PendingToolCall, type PonchoConfig, PonchoFsAdapter, PostgresEngine, type ProviderConfig, type Recurrence, type RecurrenceType, type Reminder, type ReminderCreateInput, type ReminderStatus, type ReminderStore, type RemoteMcpServerConfig, type RunConversationTurnOpts, type RunConversationTurnResult, type RunOutcome, type RunRequest, type RuntimeRenderContext, S3UploadStore, STALE_SUBAGENT_THRESHOLD_MS, STORAGE_SCHEMA_VERSION, type SecretsStore, type SkillContextEntry, type SkillMetadata, type SkillSource, SqliteEngine, type StateConfig, type StateProviderName, type StateStore, type StorageConfig, type StorageEngine, type StorageFactoryOptions, type StorageProvider, type StoredApproval, type SubagentManager, type SubagentResult, type SubagentSpawnResult, type SubagentSummary, type SubagentTranscript, type SubagentTranscriptMode, TOOL_RESULT_ARCHIVE_PARAM, type TelemetryConfig, TelemetryEmitter, type TenantTokenPayload, type ToolAccess, type ToolCall, ToolDispatcher, type ToolExecutionResult, type TurnDraftState, type TurnResultMetadata, type TurnSection, type UploadStore, type UploadsConfig, VFS_SCHEME, VercelBlobUploadStore, type VfsDirEntry, type VfsStat, type VirtualMount, applyTurnMetadata, buildAgentDirectoryName, buildApprovalCheckpoints, buildAssistantMetadata, buildSkillContextWindow, buildToolCompletedText, cloneSections, compactMessages, completeOpenAICodexDeviceAuth, computeNextOccurrence, createBashTool, createConversationStore, createConversationStoreFromEngine, createDefaultTools, createDeleteDirectoryTool, createDeleteTool, createEditTool, createMemoryStore, createMemoryStoreFromEngine, createMemoryTools, createModelProvider, createReminderStore, createReminderStoreFromEngine, createReminderTools, createSearchTools, createSecretsStore, createSkillTools, createStateStore, createStorageEngine, createSubagentTools, createTodoStoreFromEngine, createTurnDraftState, createUploadStore, createWriteTool, decodeFileInputData, defaultAgentDefinition, deleteOpenAICodexSession, deriveUploadKey, ensureAgentIdentity, estimateTokens, estimateTotalTokens, executeConversationTurn, findSafeSplitPoint, flushTurnDraft, generateAgentId, getAgentStoreDirectory, getModelContextWindow, getOpenAICodexAccessToken, getOpenAICodexAuthFilePath, getOpenAICodexRequiredScopes, getPonchoStoreRoot, isMessageArray, jsonSchemaToZod, loadCanonicalHistory, loadPonchoConfig, loadRunHistory, loadSkillContext, loadSkillInstructions, loadSkillMetadata, loadSkillMetadataFromDirs, loadVfsSkillMetadata, mergeSkills, normalizeApprovalCheckpoint, normalizeOtlp, normalizeScriptPolicyPath, normalizeToolAccess, parseAgentFile, parseAgentMarkdown, parseSkillFrontmatter, ponchoDocsTool, readOpenAICodexSession, readSkillResource, recordStandardTurnEvent, renderAgentPrompt, resolveAgentIdentity, resolveCompactionConfig, resolveEnv, resolveMemoryConfig, resolveRunRequest, resolveSkillDirs, resolveStateConfig, runConversationTurn, slugifyStorageComponent, startOpenAICodexDeviceAuth, verifyTenantToken, withToolResultArchiveParam, writeOpenAICodexSession };
|
package/dist/index.js
CHANGED
|
@@ -544,7 +544,8 @@ var resolveMemoryConfig = (config) => {
|
|
|
544
544
|
table: config.storage.table,
|
|
545
545
|
region: config.storage.region,
|
|
546
546
|
ttl: resolveTtl(config.storage.ttl, "memory"),
|
|
547
|
-
maxRecallConversations: config.storage.memory?.maxRecallConversations ?? config.memory?.maxRecallConversations
|
|
547
|
+
maxRecallConversations: config.storage.memory?.maxRecallConversations ?? config.memory?.maxRecallConversations,
|
|
548
|
+
maxPromptChars: config.storage.memory?.maxPromptChars ?? config.memory?.maxPromptChars
|
|
548
549
|
};
|
|
549
550
|
}
|
|
550
551
|
return config?.memory;
|
|
@@ -7363,7 +7364,9 @@ var collectSkillManifests = async (directory) => {
|
|
|
7363
7364
|
return files;
|
|
7364
7365
|
};
|
|
7365
7366
|
var loadSkillMetadata = async (workingDir, extraSkillPaths) => {
|
|
7366
|
-
|
|
7367
|
+
return loadSkillMetadataFromDirs(resolveSkillDirs(workingDir, extraSkillPaths));
|
|
7368
|
+
};
|
|
7369
|
+
var loadSkillMetadataFromDirs = async (skillDirs) => {
|
|
7367
7370
|
const allManifests = [];
|
|
7368
7371
|
for (const dir of skillDirs) {
|
|
7369
7372
|
try {
|
|
@@ -9120,6 +9123,8 @@ var AgentHarness = class _AgentHarness {
|
|
|
9120
9123
|
loadedConfig;
|
|
9121
9124
|
injectedConfig;
|
|
9122
9125
|
loadedSkills = [];
|
|
9126
|
+
systemSkills = [];
|
|
9127
|
+
systemSkillPaths = [];
|
|
9123
9128
|
skillFingerprint = "";
|
|
9124
9129
|
lastSkillRefreshAt = 0;
|
|
9125
9130
|
activeSkillNames = /* @__PURE__ */ new Set();
|
|
@@ -9325,6 +9330,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9325
9330
|
this.injectedStorageEngine = true;
|
|
9326
9331
|
}
|
|
9327
9332
|
this.virtualMounts = options.virtualMounts ?? [];
|
|
9333
|
+
this.systemSkillPaths = options.systemSkillPaths ?? [];
|
|
9328
9334
|
if (options.toolDefinitions?.length) {
|
|
9329
9335
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
9330
9336
|
}
|
|
@@ -9486,14 +9492,19 @@ var AgentHarness = class _AgentHarness {
|
|
|
9486
9492
|
return [...this.activeSkillNames].sort();
|
|
9487
9493
|
}
|
|
9488
9494
|
/**
|
|
9489
|
-
* Resolve the skill set visible to a given tenant
|
|
9490
|
-
*
|
|
9491
|
-
*
|
|
9492
|
-
*
|
|
9495
|
+
* Resolve the skill set visible to a given tenant. Three tiers, by
|
|
9496
|
+
* precedence: repo skills > the tenant's own VFS skills > platform
|
|
9497
|
+
* system skills. So a repo skill wins over a same-named VFS skill
|
|
9498
|
+
* (unchanged), and a tenant's `/skills/<name>/` overrides a same-named
|
|
9499
|
+
* system skill (the deploy-shipped default). Cached per tenant; cache
|
|
9500
|
+
* invalidates on VFS writes under /skills/ via invalidateSkillsForTenant.
|
|
9501
|
+
* System skills are static within a process, so they don't participate
|
|
9502
|
+
* in the fingerprint — but a VFS override does (it changes a /skills
|
|
9503
|
+
* path), which recomputes the cache and lets the override take effect.
|
|
9493
9504
|
*/
|
|
9494
9505
|
async getSkillsForTenant(tenantId) {
|
|
9495
9506
|
if (!this.storageEngine) {
|
|
9496
|
-
return this.loadedSkills;
|
|
9507
|
+
return mergeSkills(this.loadedSkills, this.systemSkills);
|
|
9497
9508
|
}
|
|
9498
9509
|
const effectiveTenant = tenantId || "__default__";
|
|
9499
9510
|
const engineWithRefresh = this.storageEngine;
|
|
@@ -9506,7 +9517,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9506
9517
|
return cached.skills;
|
|
9507
9518
|
}
|
|
9508
9519
|
const vfsSkills = await loadVfsSkillMetadata(this.storageEngine, effectiveTenant);
|
|
9509
|
-
const
|
|
9520
|
+
const repoAndVfs = mergeSkills(this.loadedSkills, vfsSkills, (skipped) => {
|
|
9510
9521
|
const key = `${effectiveTenant}:${skipped.name}`;
|
|
9511
9522
|
if (this.vfsSkillCollisionWarnings.has(key)) return;
|
|
9512
9523
|
this.vfsSkillCollisionWarnings.add(key);
|
|
@@ -9514,6 +9525,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9514
9525
|
`VFS skill "${skipped.name}" for tenant ${effectiveTenant} ignored: a repo skill with the same name takes precedence.`
|
|
9515
9526
|
);
|
|
9516
9527
|
});
|
|
9528
|
+
const merged = mergeSkills(repoAndVfs, this.systemSkills);
|
|
9517
9529
|
this.skillCache.set(effectiveTenant, { skills: merged, fingerprint });
|
|
9518
9530
|
return merged;
|
|
9519
9531
|
}
|
|
@@ -9844,6 +9856,7 @@ var AgentHarness = class _AgentHarness {
|
|
|
9844
9856
|
const extraSkillPaths = config?.skillPaths;
|
|
9845
9857
|
const skillMetadata = await loadSkillMetadata(this.workingDir, extraSkillPaths);
|
|
9846
9858
|
this.loadedSkills = skillMetadata;
|
|
9859
|
+
this.systemSkills = this.systemSkillPaths.length ? await loadSkillMetadataFromDirs(this.systemSkillPaths) : [];
|
|
9847
9860
|
this.skillFingerprint = this.buildSkillFingerprint(skillMetadata);
|
|
9848
9861
|
this.registerSkillTools();
|
|
9849
9862
|
const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
|
|
@@ -10221,7 +10234,8 @@ Browser sessions (cookies, localStorage, login state) are automatically saved an
|
|
|
10221
10234
|
### Tabs and resources
|
|
10222
10235
|
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.` : "";
|
|
10223
10236
|
const mainMemory = await memoryPromise;
|
|
10224
|
-
const
|
|
10237
|
+
const memCap = this.memoryConfig?.maxPromptChars ?? 0;
|
|
10238
|
+
const boundedMainMemory = mainMemory && memCap > 0 && mainMemory.content.length > memCap ? `${mainMemory.content.slice(0, memCap)}
|
|
10225
10239
|
...[truncated]` : mainMemory?.content;
|
|
10226
10240
|
const memoryContext = boundedMainMemory && boundedMainMemory.trim().length > 0 ? `
|
|
10227
10241
|
## Persistent Memory
|
|
@@ -13980,6 +13994,7 @@ export {
|
|
|
13980
13994
|
loadSkillContext,
|
|
13981
13995
|
loadSkillInstructions,
|
|
13982
13996
|
loadSkillMetadata,
|
|
13997
|
+
loadSkillMetadataFromDirs,
|
|
13983
13998
|
loadVfsSkillMetadata,
|
|
13984
13999
|
mergeSkills,
|
|
13985
14000
|
normalizeApprovalCheckpoint,
|
package/package.json
CHANGED
package/src/config.ts
CHANGED
|
@@ -21,6 +21,7 @@ export interface StorageConfig {
|
|
|
21
21
|
memory?: {
|
|
22
22
|
enabled?: boolean;
|
|
23
23
|
maxRecallConversations?: number;
|
|
24
|
+
maxPromptChars?: number;
|
|
24
25
|
};
|
|
25
26
|
limits?: {
|
|
26
27
|
maxFileSize?: number;
|
|
@@ -335,6 +336,9 @@ export const resolveMemoryConfig = (
|
|
|
335
336
|
maxRecallConversations:
|
|
336
337
|
config.storage.memory?.maxRecallConversations ??
|
|
337
338
|
config.memory?.maxRecallConversations,
|
|
339
|
+
maxPromptChars:
|
|
340
|
+
config.storage.memory?.maxPromptChars ??
|
|
341
|
+
config.memory?.maxPromptChars,
|
|
338
342
|
};
|
|
339
343
|
}
|
|
340
344
|
return config?.memory;
|
package/src/harness.ts
CHANGED
|
@@ -55,6 +55,7 @@ import { createModelProvider, getModelContextWindow, type ModelProviderFactory,
|
|
|
55
55
|
import {
|
|
56
56
|
buildSkillContextWindow,
|
|
57
57
|
loadSkillMetadata,
|
|
58
|
+
loadSkillMetadataFromDirs,
|
|
58
59
|
loadVfsSkillMetadata,
|
|
59
60
|
mergeSkills,
|
|
60
61
|
} from "./skill-context.js";
|
|
@@ -134,6 +135,17 @@ export interface HarnessOptions {
|
|
|
134
135
|
* Empty by default — no system mounts in the CLI / dev workflow.
|
|
135
136
|
*/
|
|
136
137
|
virtualMounts?: VirtualMount[];
|
|
138
|
+
/**
|
|
139
|
+
* Absolute directories of platform-shipped "system" skills. Each is
|
|
140
|
+
* scanned for `<name>/SKILL.md` at init; the bodies live on local disk
|
|
141
|
+
* and ship with the deploy. System skills are surfaced in
|
|
142
|
+
* `<available_skills>` like any other skill, but sit at the LOWEST
|
|
143
|
+
* precedence: a tenant's own `/skills/<same-name>/` (and a repo skill)
|
|
144
|
+
* overrides a system skill of the same name. Pair with a read-only
|
|
145
|
+
* `virtualMounts` entry (e.g. "/system/skills/") if the same files
|
|
146
|
+
* should also be browsable in the VFS. Empty by default.
|
|
147
|
+
*/
|
|
148
|
+
systemSkillPaths?: string[];
|
|
137
149
|
}
|
|
138
150
|
|
|
139
151
|
export interface HarnessRunOutput {
|
|
@@ -839,6 +851,8 @@ export class AgentHarness {
|
|
|
839
851
|
private loadedConfig?: PonchoConfig;
|
|
840
852
|
private readonly injectedConfig?: PonchoConfig;
|
|
841
853
|
private loadedSkills: SkillMetadata[] = [];
|
|
854
|
+
private systemSkills: SkillMetadata[] = [];
|
|
855
|
+
private readonly systemSkillPaths: string[] = [];
|
|
842
856
|
private skillFingerprint = "";
|
|
843
857
|
private lastSkillRefreshAt = 0;
|
|
844
858
|
private readonly activeSkillNames = new Set<string>();
|
|
@@ -1077,6 +1091,7 @@ export class AgentHarness {
|
|
|
1077
1091
|
this.injectedStorageEngine = true;
|
|
1078
1092
|
}
|
|
1079
1093
|
this.virtualMounts = options.virtualMounts ?? [];
|
|
1094
|
+
this.systemSkillPaths = options.systemSkillPaths ?? [];
|
|
1080
1095
|
|
|
1081
1096
|
if (options.toolDefinitions?.length) {
|
|
1082
1097
|
this.dispatcher.registerMany(options.toolDefinitions);
|
|
@@ -1271,14 +1286,19 @@ export class AgentHarness {
|
|
|
1271
1286
|
}
|
|
1272
1287
|
|
|
1273
1288
|
/**
|
|
1274
|
-
* Resolve the skill set visible to a given tenant
|
|
1275
|
-
*
|
|
1276
|
-
*
|
|
1277
|
-
*
|
|
1289
|
+
* Resolve the skill set visible to a given tenant. Three tiers, by
|
|
1290
|
+
* precedence: repo skills > the tenant's own VFS skills > platform
|
|
1291
|
+
* system skills. So a repo skill wins over a same-named VFS skill
|
|
1292
|
+
* (unchanged), and a tenant's `/skills/<name>/` overrides a same-named
|
|
1293
|
+
* system skill (the deploy-shipped default). Cached per tenant; cache
|
|
1294
|
+
* invalidates on VFS writes under /skills/ via invalidateSkillsForTenant.
|
|
1295
|
+
* System skills are static within a process, so they don't participate
|
|
1296
|
+
* in the fingerprint — but a VFS override does (it changes a /skills
|
|
1297
|
+
* path), which recomputes the cache and lets the override take effect.
|
|
1278
1298
|
*/
|
|
1279
1299
|
private async getSkillsForTenant(tenantId: string | undefined | null): Promise<SkillMetadata[]> {
|
|
1280
1300
|
if (!this.storageEngine) {
|
|
1281
|
-
return this.loadedSkills;
|
|
1301
|
+
return mergeSkills(this.loadedSkills, this.systemSkills);
|
|
1282
1302
|
}
|
|
1283
1303
|
// Mirror the rest of the harness: undefined tenantId falls back to
|
|
1284
1304
|
// "__default__" so dev-mode (no auth) conversations see the same VFS
|
|
@@ -1305,7 +1325,7 @@ export class AgentHarness {
|
|
|
1305
1325
|
return cached.skills;
|
|
1306
1326
|
}
|
|
1307
1327
|
const vfsSkills = await loadVfsSkillMetadata(this.storageEngine, effectiveTenant);
|
|
1308
|
-
const
|
|
1328
|
+
const repoAndVfs = mergeSkills(this.loadedSkills, vfsSkills, (skipped) => {
|
|
1309
1329
|
const key = `${effectiveTenant}:${skipped.name}`;
|
|
1310
1330
|
if (this.vfsSkillCollisionWarnings.has(key)) return;
|
|
1311
1331
|
this.vfsSkillCollisionWarnings.add(key);
|
|
@@ -1313,6 +1333,11 @@ export class AgentHarness {
|
|
|
1313
1333
|
`VFS skill "${skipped.name}" for tenant ${effectiveTenant} ignored: a repo skill with the same name takes precedence.`,
|
|
1314
1334
|
);
|
|
1315
1335
|
});
|
|
1336
|
+
// System skills sit at the bottom: a repo or VFS skill of the same
|
|
1337
|
+
// name overrides them. Overriding a system default is the intended
|
|
1338
|
+
// user workflow (mirrors /jobs system-default overrides), so the
|
|
1339
|
+
// collision is silent — not a warning.
|
|
1340
|
+
const merged = mergeSkills(repoAndVfs, this.systemSkills);
|
|
1316
1341
|
this.skillCache.set(effectiveTenant, { skills: merged, fingerprint });
|
|
1317
1342
|
return merged;
|
|
1318
1343
|
}
|
|
@@ -1706,6 +1731,13 @@ export class AgentHarness {
|
|
|
1706
1731
|
const extraSkillPaths = config?.skillPaths;
|
|
1707
1732
|
const skillMetadata = await loadSkillMetadata(this.workingDir, extraSkillPaths);
|
|
1708
1733
|
this.loadedSkills = skillMetadata;
|
|
1734
|
+
// Platform-shipped system skills, scanned from absolute dirs on disk.
|
|
1735
|
+
// Loaded once at init (they ship with the deploy and don't change
|
|
1736
|
+
// within a process). Merged at LOWEST precedence per tenant — see
|
|
1737
|
+
// getSkillsForTenant.
|
|
1738
|
+
this.systemSkills = this.systemSkillPaths.length
|
|
1739
|
+
? await loadSkillMetadataFromDirs(this.systemSkillPaths)
|
|
1740
|
+
: [];
|
|
1709
1741
|
this.skillFingerprint = this.buildSkillFingerprint(skillMetadata);
|
|
1710
1742
|
this.registerSkillTools();
|
|
1711
1743
|
const agentId = this.parsedAgent.frontmatter.id ?? this.parsedAgent.frontmatter.name;
|
|
@@ -2152,9 +2184,15 @@ Browser sessions (cookies, localStorage, login state) are automatically saved an
|
|
|
2152
2184
|
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.`
|
|
2153
2185
|
: "";
|
|
2154
2186
|
const mainMemory = await memoryPromise;
|
|
2187
|
+
// Main memory is injected in full by default — silently dropping the
|
|
2188
|
+
// tail of a user's memory every turn is a footgun. Set
|
|
2189
|
+
// `maxPromptChars` to a positive number to opt into a cap (e.g. for
|
|
2190
|
+
// prompt-cost control); content beyond it is sliced with a
|
|
2191
|
+
// `...[truncated]` marker.
|
|
2192
|
+
const memCap = this.memoryConfig?.maxPromptChars ?? 0;
|
|
2155
2193
|
const boundedMainMemory =
|
|
2156
|
-
mainMemory && mainMemory.content.length >
|
|
2157
|
-
? `${mainMemory.content.slice(0,
|
|
2194
|
+
mainMemory && memCap > 0 && mainMemory.content.length > memCap
|
|
2195
|
+
? `${mainMemory.content.slice(0, memCap)}\n...[truncated]`
|
|
2158
2196
|
: mainMemory?.content;
|
|
2159
2197
|
const memoryContext =
|
|
2160
2198
|
boundedMainMemory && boundedMainMemory.trim().length > 0
|
package/src/memory.ts
CHANGED
|
@@ -15,6 +15,15 @@ export interface MemoryConfig {
|
|
|
15
15
|
region?: string;
|
|
16
16
|
ttl?: number;
|
|
17
17
|
maxRecallConversations?: number;
|
|
18
|
+
/**
|
|
19
|
+
* Optional cap on the characters of main memory injected into the
|
|
20
|
+
* system prompt each turn. Default is **no cap** — the full memory is
|
|
21
|
+
* injected (silently truncating a user's memory every turn is a
|
|
22
|
+
* footgun). Set a positive number to opt into truncation for
|
|
23
|
+
* prompt-cost control; content beyond it is sliced with a
|
|
24
|
+
* `...[truncated]` marker.
|
|
25
|
+
*/
|
|
26
|
+
maxPromptChars?: number;
|
|
18
27
|
}
|
|
19
28
|
|
|
20
29
|
export interface MemoryStore {
|
package/src/skill-context.ts
CHANGED
|
@@ -209,7 +209,17 @@ export const loadSkillMetadata = async (
|
|
|
209
209
|
workingDir: string,
|
|
210
210
|
extraSkillPaths?: string[],
|
|
211
211
|
): Promise<SkillMetadata[]> => {
|
|
212
|
-
|
|
212
|
+
return loadSkillMetadataFromDirs(resolveSkillDirs(workingDir, extraSkillPaths));
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
// Scan an explicit list of absolute directories for `<name>/SKILL.md`
|
|
216
|
+
// manifests and return their metadata as `source: "repo"` skills (body
|
|
217
|
+
// read from disk on activation). Used both by `loadSkillMetadata` (after
|
|
218
|
+
// resolving repo skill dirs against the working dir) and directly for
|
|
219
|
+
// platform-shipped "system" skills whose source dirs are already absolute.
|
|
220
|
+
export const loadSkillMetadataFromDirs = async (
|
|
221
|
+
skillDirs: string[],
|
|
222
|
+
): Promise<SkillMetadata[]> => {
|
|
213
223
|
const allManifests: string[] = [];
|
|
214
224
|
|
|
215
225
|
for (const dir of skillDirs) {
|