salmon-loop 0.2.3 → 0.2.16
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/argv/headless-detection.js +27 -0
- package/dist/cli/chat-flow.js +11 -0
- package/dist/cli/chat.js +161 -24
- package/dist/cli/commands/chat.js +30 -24
- package/dist/cli/commands/context.js +15 -3
- package/dist/cli/commands/flow-mode.js +63 -0
- package/dist/cli/commands/help-format.js +12 -0
- package/dist/cli/commands/registry.js +6 -7
- package/dist/cli/commands/run/benchmark-artifacts.js +41 -0
- package/dist/cli/commands/run/config-resolution.js +30 -24
- package/dist/cli/commands/run/early-errors.js +23 -0
- package/dist/cli/commands/run/handler.js +131 -44
- package/dist/cli/commands/run/headless-error-writer.js +8 -0
- package/dist/cli/commands/run/loop-params.js +3 -0
- package/dist/cli/commands/run/mode.js +2 -5
- package/dist/cli/commands/run/parse-options.js +18 -2
- package/dist/cli/commands/run/persist-session.js +10 -1
- package/dist/cli/commands/run/preflight.js +10 -0
- package/dist/cli/commands/run/reporter-factory.js +4 -0
- package/dist/cli/commands/run/runtime-llm.js +38 -11
- package/dist/cli/commands/run/runtime-options.js +2 -2
- package/dist/cli/commands/run/validate-options.js +0 -5
- package/dist/cli/commands/run/verbose.js +2 -7
- package/dist/cli/commands/serve.js +117 -90
- package/dist/cli/commands/tool-names.js +78 -78
- package/dist/cli/headless/anthropic-stream-normalized-encoder.js +6 -1
- package/dist/cli/headless/json-protocol.js +37 -0
- package/dist/cli/headless/native-stream-normalized-encoder.js +6 -1
- package/dist/cli/headless/protocol-metadata.js +22 -0
- package/dist/cli/headless/stream-json-protocol.js +34 -1
- package/dist/cli/index.js +6 -4
- package/dist/cli/locales/en.js +32 -6
- package/dist/cli/program-bootstrap.js +14 -4
- package/dist/cli/program-commands.js +9 -1
- package/dist/cli/program-options.js +1 -0
- package/dist/cli/reporters/anthropic-stream.js +7 -1
- package/dist/cli/reporters/json.js +4 -0
- package/dist/cli/reporters/stream-json.js +17 -2
- package/dist/cli/run-cli.js +5 -3
- package/dist/cli/slash/runtime.js +30 -15
- package/dist/cli/ui/components/CommandInput.js +7 -3
- package/dist/cli/ui/components/CommandSuggestionList.js +1 -1
- package/dist/cli/utils/command-option-source.js +13 -0
- package/dist/cli/utils/output-format.js +6 -0
- package/dist/cli/utils/resolve-cli-config.js +98 -0
- package/dist/cli/utils/verbose-level.js +8 -0
- package/dist/cli/utils/verify-resolver.js +8 -4
- package/dist/cli/utils/worktree-prepare-resolver.js +7 -3
- package/dist/core/adapters/fs/file-adapter.js +6 -0
- package/dist/core/adapters/fs/filesystem.js +2 -1
- package/dist/core/adapters/git/git-adapter.js +78 -1
- package/dist/core/benchmark/patch-artifact.js +124 -0
- package/dist/core/benchmark/swe-bench.js +25 -0
- package/dist/core/config/load.js +39 -18
- package/dist/core/config/merge.js +27 -0
- package/dist/core/config/paths.js +24 -5
- package/dist/core/config/resolve-llm.js +12 -0
- package/dist/core/config/resolve.js +7 -5
- package/dist/core/config/resolvers/server.js +0 -6
- package/dist/core/config/validate.js +94 -21
- package/dist/core/context/gatherers/metadata-gatherer.js +1 -0
- package/dist/core/context/gatherers/ripgrep-gatherer.js +84 -2
- package/dist/core/context/keywords.js +18 -4
- package/dist/core/context/service-deps.js +2 -2
- package/dist/core/context/service.js +8 -0
- package/dist/core/context/steps/context-gather.js +38 -0
- package/dist/core/context/summarization/summarizer.js +55 -12
- package/dist/core/context/targeting/target-resolver.js +4 -4
- package/dist/core/extensions/index.js +23 -5
- package/dist/core/extensions/paths.js +31 -0
- package/dist/core/extensions/schemas.js +8 -5
- package/dist/core/facades/cli-chat.js +6 -2
- package/dist/core/facades/cli-command-chat.js +2 -1
- package/dist/core/facades/cli-command-tool-names.js +2 -0
- package/dist/core/facades/cli-context.js +1 -0
- package/dist/core/facades/cli-observability.js +1 -1
- package/dist/core/facades/cli-run-handler.js +4 -2
- package/dist/core/facades/cli-run-persist-session.js +1 -0
- package/dist/core/facades/cli-serve.js +2 -4
- package/dist/core/facades/cli-utils-worktree.js +1 -1
- package/dist/core/failure/diagnostics.js +53 -1
- package/dist/core/grizzco/dsl/llm-strategy.js +4 -1
- package/dist/core/grizzco/engine/outcome/loop-result-mapper.js +67 -9
- package/dist/core/grizzco/engine/pipeline/pipeline.js +6 -2
- package/dist/core/grizzco/engine/transaction/attempt-failure.js +90 -15
- package/dist/core/grizzco/engine/transaction/report-mapper.js +17 -3
- package/dist/core/grizzco/engine/transaction/transaction-runner.js +173 -7
- package/dist/core/grizzco/flows/AutopilotFlow.js +18 -0
- package/dist/core/grizzco/flows/flow-dispatch.js +11 -0
- package/dist/core/grizzco/steps/answer.js +13 -14
- package/dist/core/grizzco/steps/autopilot.js +396 -0
- package/dist/core/grizzco/steps/cache-sharing.js +29 -0
- package/dist/core/grizzco/steps/explore.js +37 -21
- package/dist/core/grizzco/steps/generateReview.js +2 -5
- package/dist/core/grizzco/steps/patch/apply-check.js +10 -0
- package/dist/core/grizzco/steps/patch/diff-normalization.js +70 -0
- package/dist/core/grizzco/steps/patch/diff-salvage.js +46 -0
- package/dist/core/grizzco/steps/patch/prompt-input.js +42 -0
- package/dist/core/grizzco/steps/patch.js +105 -146
- package/dist/core/grizzco/steps/plan.js +101 -25
- package/dist/core/grizzco/steps/preflight.js +5 -3
- package/dist/core/grizzco/steps/request-assembly.js +78 -0
- package/dist/core/grizzco/steps/research.js +39 -36
- package/dist/core/grizzco/steps/tool-runtime.js +47 -0
- package/dist/core/grizzco/steps/verify-shared.js +23 -0
- package/dist/core/grizzco/steps/verify.js +13 -21
- package/dist/core/intent/chat-intent.js +0 -4
- package/dist/core/llm/ai-sdk/chat-executor.js +2 -0
- package/dist/core/llm/ai-sdk/high-level-phase-specs.js +63 -0
- package/dist/core/llm/ai-sdk/message-mapper.js +40 -10
- package/dist/core/llm/ai-sdk/provider-factory.js +14 -0
- package/dist/core/llm/ai-sdk/request-params.js +74 -1
- package/dist/core/llm/ai-sdk/result-mapper.js +16 -0
- package/dist/core/llm/ai-sdk.js +112 -27
- package/dist/core/llm/capabilities.js +12 -0
- package/dist/core/llm/contracts/repair.js +36 -30
- package/dist/core/llm/errors.js +83 -2
- package/dist/core/llm/message-composition.js +7 -22
- package/dist/core/llm/phase-router.js +29 -10
- package/dist/core/llm/redact.js +28 -3
- package/dist/core/llm/registry.js +2 -0
- package/dist/core/llm/request-augmentation.js +55 -0
- package/dist/core/llm/request-envelope.js +334 -0
- package/dist/core/llm/shared-request-assembly.js +35 -0
- package/dist/core/llm/stream-utils.js +13 -4
- package/dist/core/llm/utils.js +18 -29
- package/dist/core/memory/relevant-retrieval.js +144 -0
- package/dist/core/observability/logger.js +11 -2
- package/dist/core/patch/diff.js +1 -0
- package/dist/core/prompts/registry.js +39 -2
- package/dist/core/prompts/runtime.js +50 -12
- package/dist/core/prompts/templates/phases/patch_user.hbs +2 -5
- package/dist/core/prompts/templates/phases/research_user.hbs +11 -0
- package/dist/core/prompts/templates/phases/review_user.hbs +3 -0
- package/dist/core/prompts/templates/system/answer_system.hbs +5 -0
- package/dist/core/prompts/templates/system/autopilot_system.hbs +11 -0
- package/dist/core/prompts/templates/system/explore_system.hbs +14 -23
- package/dist/core/prompts/templates/system/main_system.hbs +4 -16
- package/dist/core/prompts/templates/system/patch_system.hbs +39 -8
- package/dist/core/prompts/templates/system/plan_system.hbs +86 -1
- package/dist/core/prompts/templates/system/research_system.hbs +2 -0
- package/dist/core/protocols/a2a/agent-card.js +3 -2
- package/dist/core/protocols/a2a/sdk/executor.js +8 -6
- package/dist/core/protocols/a2a/sdk/server.js +0 -1
- package/dist/core/protocols/acp/formal-agent.js +221 -55
- package/dist/core/protocols/acp/handlers.js +5 -1
- package/dist/core/protocols/acp/permission-provider.js +21 -1
- package/dist/core/protocols/shared/execution-request.js +24 -0
- package/dist/core/protocols/shared/flow-mode-mapping.js +23 -0
- package/dist/core/public-capabilities/flow-mode-metadata.js +39 -0
- package/dist/core/public-capabilities/projections.js +29 -0
- package/dist/core/public-capabilities/registry.js +26 -0
- package/dist/core/public-capabilities/types.js +2 -0
- package/dist/core/runtime/agent-server-runtime.js +47 -43
- package/dist/core/runtime/execution-profile.js +67 -0
- package/dist/core/session/artifact-state.js +160 -0
- package/dist/core/session/compaction/index.js +183 -0
- package/dist/core/session/compaction/microcompact.js +78 -0
- package/dist/core/session/compaction/tracking.js +48 -0
- package/dist/core/session/compaction/types.js +11 -0
- package/dist/core/session/compression.js +12 -4
- package/dist/core/session/manager.js +247 -10
- package/dist/core/session/pruning-strategy.js +55 -9
- package/dist/core/session/replacement-preview-provider.js +24 -0
- package/dist/core/session/replacement-state.js +131 -0
- package/dist/core/session/resume-repair/pipeline.js +79 -0
- package/dist/core/session/resume-repair/stages/load-raw-archive-state.js +40 -0
- package/dist/core/session/resume-repair/stages/reattach-runtime-state.js +8 -0
- package/dist/core/session/resume-repair/stages/recover-orphaned-branches.js +10 -0
- package/dist/core/session/resume-repair/stages/relink-boundary-and-tail.js +36 -0
- package/dist/core/session/resume-repair/stages/replay-startup-hooks.js +23 -0
- package/dist/core/session/resume-repair/stages/rescue-stale-metadata.js +17 -0
- package/dist/core/session/resume-repair/types.js +2 -0
- package/dist/core/session/summary-sync.js +164 -13
- package/dist/core/session/token-tracker.js +6 -0
- package/dist/core/skills/audit.js +34 -0
- package/dist/core/skills/bridge.js +84 -7
- package/dist/core/skills/discovery.js +94 -0
- package/dist/core/skills/feature-flags.js +52 -0
- package/dist/core/skills/index.js +1 -1
- package/dist/core/skills/loader.js +195 -20
- package/dist/core/skills/parser.js +296 -24
- package/dist/core/skills/permissions.js +117 -0
- package/dist/core/skills/runtime/MicroTaskRunner.js +10 -4
- package/dist/core/skills/runtime/SkillRunner.js +240 -61
- package/dist/core/strata/layers/shadow-driver/shadow-driver.js +37 -7
- package/dist/core/strata/layers/worktree.js +70 -13
- package/dist/core/strata/runtime/synchronizer.js +29 -2
- package/dist/core/streaming/stream-assembler.js +75 -31
- package/dist/core/sub-agent/context-snapshot.js +156 -0
- package/dist/core/sub-agent/core/loop.js +1 -1
- package/dist/core/sub-agent/core/manager.js +119 -20
- package/dist/core/sub-agent/dispatch-policy.js +29 -0
- package/dist/core/sub-agent/prefix-consistency.js +48 -0
- package/dist/core/sub-agent/registry-defaults.js +4 -0
- package/dist/core/sub-agent/tools/task-spawn.js +79 -2
- package/dist/core/sub-agent/types.js +134 -5
- package/dist/core/tools/audit.js +13 -4
- package/dist/core/tools/builtin/ast-grep.js +1 -1
- package/dist/core/tools/builtin/ast.js +1 -1
- package/dist/core/tools/builtin/benchmark.js +360 -0
- package/dist/core/tools/builtin/code-search/backends/rg.js +2 -1
- package/dist/core/tools/builtin/code-search/executor.js +6 -1
- package/dist/core/tools/builtin/code-search/spec.js +26 -2
- package/dist/core/tools/builtin/fs.js +256 -23
- package/dist/core/tools/builtin/git.js +2 -2
- package/dist/core/tools/builtin/index.js +51 -2
- package/dist/core/tools/builtin/interaction.js +8 -1
- package/dist/core/tools/builtin/plan.js +37 -15
- package/dist/core/tools/builtin/shell.js +1 -1
- package/dist/core/tools/loader.js +39 -16
- package/dist/core/tools/mapper.js +17 -3
- package/dist/core/tools/parallel/scheduler.js +35 -4
- package/dist/core/tools/permissions/permission-rules.js +5 -10
- package/dist/core/tools/policy.js +6 -1
- package/dist/core/tools/recoverable-tool-errors.js +10 -0
- package/dist/core/tools/router.js +24 -6
- package/dist/core/tools/session.js +458 -48
- package/dist/core/tools/tool-visibility.js +62 -0
- package/dist/core/tools/types.js +9 -1
- package/dist/core/types/execution.js +4 -0
- package/dist/core/types/flow-mode.js +8 -0
- package/dist/core/utils/path.js +52 -0
- package/dist/core/verification/runner.js +4 -1
- package/dist/interfaces/cli/task-runner.js +4 -3
- package/dist/languages/typescript/index.js +4 -1
- package/dist/locales/en.js +87 -2
- package/dist/utils/eol.js +1 -1
- package/package.json +15 -8
- package/scripts/fix-es-abstract-compat.js +77 -0
- package/dist/core/runtime/fastify-server-bundle.js +0 -26
- package/dist/core/runtime/sidecar-fastify-plugin.js +0 -35
- package/dist/core/runtime/sidecar-paths.js +0 -47
- package/dist/core/runtime/sidecar-route-catalog.js +0 -103
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
export const TOOL_RESULT_REPLACEMENT_IDENTITY_VERSION = 'v1';
|
|
3
|
+
export const TOOL_RESULT_REPLACEMENT_HASH_ALGORITHM = 'sha256';
|
|
4
|
+
export const TOOL_RESULT_REPLACEMENT_STATE_SCHEMA_VERSION = 1;
|
|
5
|
+
const DEFAULT_MAX_ENTRIES = 256;
|
|
6
|
+
function normalizeNewlines(value) {
|
|
7
|
+
return value.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
8
|
+
}
|
|
9
|
+
function canonicalize(value) {
|
|
10
|
+
if (value === null)
|
|
11
|
+
return 'null';
|
|
12
|
+
if (typeof value === 'string') {
|
|
13
|
+
return JSON.stringify(normalizeNewlines(value));
|
|
14
|
+
}
|
|
15
|
+
if (typeof value === 'number') {
|
|
16
|
+
if (!Number.isFinite(value))
|
|
17
|
+
return JSON.stringify(String(value));
|
|
18
|
+
return JSON.stringify(Number(value));
|
|
19
|
+
}
|
|
20
|
+
if (typeof value === 'boolean')
|
|
21
|
+
return value ? 'true' : 'false';
|
|
22
|
+
if (Array.isArray(value)) {
|
|
23
|
+
return `[${value.map((item) => canonicalize(item)).join(',')}]`;
|
|
24
|
+
}
|
|
25
|
+
if (typeof value === 'object') {
|
|
26
|
+
const record = value;
|
|
27
|
+
const keys = Object.keys(record).sort();
|
|
28
|
+
const pairs = keys.map((key) => `${JSON.stringify(key)}:${canonicalize(record[key])}`);
|
|
29
|
+
return `{${pairs.join(',')}}`;
|
|
30
|
+
}
|
|
31
|
+
return JSON.stringify(String(value));
|
|
32
|
+
}
|
|
33
|
+
export function createToolResultIdentity(params) {
|
|
34
|
+
const payloadBytes = canonicalize(params.payload);
|
|
35
|
+
const hash = createHash(TOOL_RESULT_REPLACEMENT_HASH_ALGORITHM);
|
|
36
|
+
hash.update(TOOL_RESULT_REPLACEMENT_IDENTITY_VERSION);
|
|
37
|
+
hash.update('\n');
|
|
38
|
+
hash.update(normalizeNewlines(params.canonicalToolCallIdentity).trim());
|
|
39
|
+
hash.update('\n');
|
|
40
|
+
hash.update(payloadBytes);
|
|
41
|
+
return hash.digest('hex');
|
|
42
|
+
}
|
|
43
|
+
function isValidEntry(value) {
|
|
44
|
+
if (!value || typeof value !== 'object')
|
|
45
|
+
return false;
|
|
46
|
+
const entry = value;
|
|
47
|
+
if (!entry.toolResultId || typeof entry.toolResultId !== 'string')
|
|
48
|
+
return false;
|
|
49
|
+
if (entry.decision !== 'kept' && entry.decision !== 'replaced')
|
|
50
|
+
return false;
|
|
51
|
+
if (typeof entry.preview !== 'string')
|
|
52
|
+
return false;
|
|
53
|
+
if (typeof entry.frozenAt !== 'number' || !Number.isFinite(entry.frozenAt))
|
|
54
|
+
return false;
|
|
55
|
+
if (entry.sourceArtifactHandle !== undefined && typeof entry.sourceArtifactHandle !== 'string') {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
if (entry.identityVersion !== TOOL_RESULT_REPLACEMENT_IDENTITY_VERSION)
|
|
59
|
+
return false;
|
|
60
|
+
if (entry.hashAlgorithm !== TOOL_RESULT_REPLACEMENT_HASH_ALGORITHM)
|
|
61
|
+
return false;
|
|
62
|
+
return true;
|
|
63
|
+
}
|
|
64
|
+
export function normalizeToolResultReplacementState(state) {
|
|
65
|
+
if (!state || typeof state !== 'object')
|
|
66
|
+
return undefined;
|
|
67
|
+
if (state.schemaVersion !== TOOL_RESULT_REPLACEMENT_STATE_SCHEMA_VERSION)
|
|
68
|
+
return undefined;
|
|
69
|
+
if (!state.entries || typeof state.entries !== 'object')
|
|
70
|
+
return undefined;
|
|
71
|
+
const normalizedEntries = {};
|
|
72
|
+
for (const [key, value] of Object.entries(state.entries)) {
|
|
73
|
+
if (!isValidEntry(value))
|
|
74
|
+
continue;
|
|
75
|
+
if (value.toolResultId !== key)
|
|
76
|
+
continue;
|
|
77
|
+
normalizedEntries[key] = {
|
|
78
|
+
toolResultId: value.toolResultId,
|
|
79
|
+
decision: value.decision,
|
|
80
|
+
preview: value.preview,
|
|
81
|
+
frozenAt: value.frozenAt,
|
|
82
|
+
sourceArtifactHandle: value.sourceArtifactHandle,
|
|
83
|
+
identityVersion: value.identityVersion,
|
|
84
|
+
hashAlgorithm: value.hashAlgorithm,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (Object.keys(normalizedEntries).length === 0)
|
|
88
|
+
return undefined;
|
|
89
|
+
return {
|
|
90
|
+
schemaVersion: TOOL_RESULT_REPLACEMENT_STATE_SCHEMA_VERSION,
|
|
91
|
+
entries: normalizedEntries,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export function createEmptyToolResultReplacementState() {
|
|
95
|
+
return {
|
|
96
|
+
schemaVersion: TOOL_RESULT_REPLACEMENT_STATE_SCHEMA_VERSION,
|
|
97
|
+
entries: {},
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
export function freezeToolResultReplacementDecision(state, entry, options) {
|
|
101
|
+
const base = normalizeToolResultReplacementState(state) ?? createEmptyToolResultReplacementState();
|
|
102
|
+
const existing = base.entries[entry.toolResultId];
|
|
103
|
+
if (existing) {
|
|
104
|
+
return base;
|
|
105
|
+
}
|
|
106
|
+
const nextEntries = {
|
|
107
|
+
...base.entries,
|
|
108
|
+
[entry.toolResultId]: {
|
|
109
|
+
toolResultId: entry.toolResultId,
|
|
110
|
+
decision: entry.decision,
|
|
111
|
+
preview: entry.preview,
|
|
112
|
+
frozenAt: entry.frozenAt ?? Date.now(),
|
|
113
|
+
sourceArtifactHandle: entry.sourceArtifactHandle,
|
|
114
|
+
identityVersion: TOOL_RESULT_REPLACEMENT_IDENTITY_VERSION,
|
|
115
|
+
hashAlgorithm: TOOL_RESULT_REPLACEMENT_HASH_ALGORITHM,
|
|
116
|
+
},
|
|
117
|
+
};
|
|
118
|
+
const maxEntries = Math.max(1, options?.maxEntries ?? DEFAULT_MAX_ENTRIES);
|
|
119
|
+
const keys = Object.keys(nextEntries);
|
|
120
|
+
if (keys.length > maxEntries) {
|
|
121
|
+
const sorted = keys.sort((a, b) => nextEntries[a].frozenAt - nextEntries[b].frozenAt);
|
|
122
|
+
for (const evict of sorted.slice(0, keys.length - maxEntries)) {
|
|
123
|
+
delete nextEntries[evict];
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return {
|
|
127
|
+
schemaVersion: TOOL_RESULT_REPLACEMENT_STATE_SCHEMA_VERSION,
|
|
128
|
+
entries: nextEntries,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=replacement-state.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { randomBytes } from 'crypto';
|
|
2
|
+
import { loadRawArchiveStateStage } from './stages/load-raw-archive-state.js';
|
|
3
|
+
import { reattachRuntimeStateStage } from './stages/reattach-runtime-state.js';
|
|
4
|
+
import { recoverOrphanedBranchesStage } from './stages/recover-orphaned-branches.js';
|
|
5
|
+
import { relinkBoundaryAndTailStage } from './stages/relink-boundary-and-tail.js';
|
|
6
|
+
import { replayStartupHooksStage } from './stages/replay-startup-hooks.js';
|
|
7
|
+
import { rescueStaleMetadataStage } from './stages/rescue-stale-metadata.js';
|
|
8
|
+
export function createResumeRepairPipeline(params) {
|
|
9
|
+
const context = {
|
|
10
|
+
repoPath: params.repoPath,
|
|
11
|
+
now: params.now ?? (() => Date.now()),
|
|
12
|
+
nextId: params.nextId ?? (() => randomBytes(6).toString('hex')),
|
|
13
|
+
startupHooks: params.startupHooks ?? [],
|
|
14
|
+
};
|
|
15
|
+
return {
|
|
16
|
+
async run(input) {
|
|
17
|
+
const compressed = await params.compressedStore.loadCompressed(input.filename);
|
|
18
|
+
if (!compressed) {
|
|
19
|
+
return {
|
|
20
|
+
warnings: [],
|
|
21
|
+
repairActions: [],
|
|
22
|
+
contractViolations: [
|
|
23
|
+
{
|
|
24
|
+
code: 'ARCHIVE_CORRUPTED',
|
|
25
|
+
message: `Archive "${input.archiveId}" cannot be loaded.`,
|
|
26
|
+
},
|
|
27
|
+
],
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
const partial = await params.compressor.decompressToSession(compressed);
|
|
31
|
+
const state = {
|
|
32
|
+
archiveId: input.archiveId,
|
|
33
|
+
filename: input.filename,
|
|
34
|
+
compressed,
|
|
35
|
+
partial,
|
|
36
|
+
session: {
|
|
37
|
+
meta: {
|
|
38
|
+
id: '',
|
|
39
|
+
name: '',
|
|
40
|
+
repoPath: context.repoPath,
|
|
41
|
+
createdAt: 0,
|
|
42
|
+
updatedAt: 0,
|
|
43
|
+
totalIterations: 0,
|
|
44
|
+
successfulIterations: 0,
|
|
45
|
+
totalTokens: { input: 0, output: 0 },
|
|
46
|
+
snapshots: [],
|
|
47
|
+
},
|
|
48
|
+
messages: [],
|
|
49
|
+
iterations: [],
|
|
50
|
+
},
|
|
51
|
+
warnings: [],
|
|
52
|
+
repairActions: [],
|
|
53
|
+
contractViolations: [],
|
|
54
|
+
};
|
|
55
|
+
const stages = [
|
|
56
|
+
loadRawArchiveStateStage,
|
|
57
|
+
rescueStaleMetadataStage,
|
|
58
|
+
relinkBoundaryAndTailStage,
|
|
59
|
+
recoverOrphanedBranchesStage,
|
|
60
|
+
reattachRuntimeStateStage,
|
|
61
|
+
replayStartupHooksStage,
|
|
62
|
+
];
|
|
63
|
+
for (const stage of stages) {
|
|
64
|
+
await stage(state, context);
|
|
65
|
+
if (state.contractViolations.length > 0) {
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
session: state.contractViolations.length > 0 ? undefined : state.session,
|
|
71
|
+
replacementState: state.replacementState,
|
|
72
|
+
warnings: state.warnings,
|
|
73
|
+
repairActions: state.repairActions,
|
|
74
|
+
contractViolations: state.contractViolations,
|
|
75
|
+
};
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=pipeline.js.map
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { parseFlowMode } from '../../../types/flow-mode.js';
|
|
2
|
+
import { normalizeSessionArtifactState } from '../../artifact-state.js';
|
|
3
|
+
import { normalizeToolResultReplacementState } from '../../replacement-state.js';
|
|
4
|
+
export const loadRawArchiveStateStage = async (state, context) => {
|
|
5
|
+
const partial = state.partial;
|
|
6
|
+
const flowMode = parseFlowMode(partial.meta.chatState?.flowMode);
|
|
7
|
+
const reconstructed = {
|
|
8
|
+
meta: {
|
|
9
|
+
id: partial.meta.id,
|
|
10
|
+
name: partial.meta.name,
|
|
11
|
+
repoPath: context.repoPath,
|
|
12
|
+
createdAt: partial.meta.createdAt,
|
|
13
|
+
updatedAt: context.now(),
|
|
14
|
+
totalIterations: partial.meta.totalIterations ?? partial.iterations.length,
|
|
15
|
+
successfulIterations: partial.meta.successfulIterations ?? 0,
|
|
16
|
+
totalTokens: partial.meta.totalTokens ?? { input: 0, output: 0 },
|
|
17
|
+
snapshots: [],
|
|
18
|
+
chatState: flowMode ? { flowMode } : undefined,
|
|
19
|
+
artifactState: normalizeSessionArtifactState(partial.meta.artifactState),
|
|
20
|
+
replacementState: normalizeToolResultReplacementState(partial.meta.replacementState),
|
|
21
|
+
},
|
|
22
|
+
messages: partial.messages.map((message, index) => ({
|
|
23
|
+
id: `restored-msg-${index}`,
|
|
24
|
+
role: message.role,
|
|
25
|
+
content: message.content,
|
|
26
|
+
timestamp: message.timestamp,
|
|
27
|
+
})),
|
|
28
|
+
iterations: partial.iterations.map((iteration, index) => ({
|
|
29
|
+
id: iteration.id || `restored-iter-${index + 1}`,
|
|
30
|
+
attempt: index + 1,
|
|
31
|
+
plan: null,
|
|
32
|
+
patch: null,
|
|
33
|
+
error: iteration.outcome === 'failure' ? iteration.summary : undefined,
|
|
34
|
+
contextSummary: iteration.summary,
|
|
35
|
+
})),
|
|
36
|
+
};
|
|
37
|
+
state.session = reconstructed;
|
|
38
|
+
state.replacementState = reconstructed.meta.replacementState;
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=load-raw-archive-state.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { normalizeSessionArtifactState } from '../../artifact-state.js';
|
|
2
|
+
import { normalizeToolResultReplacementState } from '../../replacement-state.js';
|
|
3
|
+
export const reattachRuntimeStateStage = async (state) => {
|
|
4
|
+
state.session.meta.artifactState = normalizeSessionArtifactState(state.session.meta.artifactState);
|
|
5
|
+
state.replacementState = normalizeToolResultReplacementState(state.replacementState);
|
|
6
|
+
state.session.meta.replacementState = state.replacementState;
|
|
7
|
+
};
|
|
8
|
+
//# sourceMappingURL=reattach-runtime-state.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const recoverOrphanedBranchesStage = async (state) => {
|
|
2
|
+
if (state.session.iterations.length > state.session.meta.totalIterations) {
|
|
3
|
+
state.session.meta.totalIterations = state.session.iterations.length;
|
|
4
|
+
state.repairActions.push({
|
|
5
|
+
code: 'RECOVERED_ITERATION_COUNT',
|
|
6
|
+
detail: 'Normalized stale totalIterations from recovered iteration list.',
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=recover-orphaned-branches.js.map
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
function hasFiniteTimestamp(value) {
|
|
2
|
+
return typeof value === 'number' && Number.isFinite(value) && value > 0;
|
|
3
|
+
}
|
|
4
|
+
export const relinkBoundaryAndTailStage = async (state) => {
|
|
5
|
+
const meta = state.session.meta;
|
|
6
|
+
if (!meta.id ||
|
|
7
|
+
!meta.name ||
|
|
8
|
+
!hasFiniteTimestamp(meta.createdAt) ||
|
|
9
|
+
!hasFiniteTimestamp(meta.updatedAt)) {
|
|
10
|
+
state.contractViolations.push({
|
|
11
|
+
code: 'MALFORMED_SESSION_BOUNDARY_METADATA',
|
|
12
|
+
message: 'Archive metadata failed boundary validation.',
|
|
13
|
+
});
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
const invalidMessage = state.session.messages.some((message) => !hasFiniteTimestamp(message.timestamp) || !message.id);
|
|
17
|
+
if (invalidMessage) {
|
|
18
|
+
state.contractViolations.push({
|
|
19
|
+
code: 'MALFORMED_MESSAGE_BOUNDARY_METADATA',
|
|
20
|
+
message: 'Recovered messages contain invalid boundary metadata.',
|
|
21
|
+
});
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const invalidIteration = state.session.iterations.some((iteration) => typeof iteration.id !== 'string' ||
|
|
25
|
+
!iteration.id ||
|
|
26
|
+
!Number.isInteger(iteration.attempt) ||
|
|
27
|
+
iteration.attempt <= 0 ||
|
|
28
|
+
typeof iteration.contextSummary !== 'string');
|
|
29
|
+
if (invalidIteration) {
|
|
30
|
+
state.contractViolations.push({
|
|
31
|
+
code: 'MALFORMED_TAIL_ITERATION_METADATA',
|
|
32
|
+
message: 'Recovered iterations contain invalid tail metadata.',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
//# sourceMappingURL=relink-boundary-and-tail.js.map
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const replayStartupHooksStage = async (state, context) => {
|
|
2
|
+
const executed = new Set();
|
|
3
|
+
for (const hook of context.startupHooks) {
|
|
4
|
+
if (!hook?.key || executed.has(hook.key))
|
|
5
|
+
continue;
|
|
6
|
+
executed.add(hook.key);
|
|
7
|
+
try {
|
|
8
|
+
await hook.run(state.session, {
|
|
9
|
+
now: context.now,
|
|
10
|
+
nextId: context.nextId,
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
catch (error) {
|
|
14
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
15
|
+
state.contractViolations.push({
|
|
16
|
+
code: 'STARTUP_HOOK_FAILED',
|
|
17
|
+
message: `Startup hook "${hook.key}" failed: ${reason}`,
|
|
18
|
+
});
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=replay-startup-hooks.js.map
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const rescueStaleMetadataStage = async (state, context) => {
|
|
2
|
+
if (!state.session.meta.name.trim()) {
|
|
3
|
+
state.session.meta.name = `Recovered ${state.session.meta.id}`;
|
|
4
|
+
state.repairActions.push({
|
|
5
|
+
code: 'RESCUED_EMPTY_NAME',
|
|
6
|
+
detail: 'Recovered missing session name from archive metadata.',
|
|
7
|
+
});
|
|
8
|
+
}
|
|
9
|
+
if (state.session.meta.updatedAt < state.session.meta.createdAt) {
|
|
10
|
+
state.session.meta.updatedAt = context.now();
|
|
11
|
+
state.repairActions.push({
|
|
12
|
+
code: 'RESCUED_UPDATED_AT',
|
|
13
|
+
detail: 'Adjusted stale updatedAt to a valid timestamp.',
|
|
14
|
+
});
|
|
15
|
+
}
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=rescue-stale-metadata.js.map
|
|
@@ -1,11 +1,100 @@
|
|
|
1
1
|
import { ConversationSummarizer } from '../context/summarization/summarizer.js';
|
|
2
2
|
import { DEFAULT_SUMMARIZATION_CONFIG } from '../context/summarization/types.js';
|
|
3
|
+
import { microcompact } from './compaction/microcompact.js';
|
|
4
|
+
import { buildSessionConversationContext } from './session-context-builder.js';
|
|
3
5
|
import { TokenTracker } from './token-tracker.js';
|
|
6
|
+
const MAX_RECOVERY_READ_FILES = 6;
|
|
7
|
+
const MAX_RECOVERY_SAFE_HINT_CHARS = 240;
|
|
8
|
+
function trimToUndefined(value) {
|
|
9
|
+
if (typeof value !== 'string')
|
|
10
|
+
return undefined;
|
|
11
|
+
const trimmed = value.trim();
|
|
12
|
+
return trimmed ? trimmed : undefined;
|
|
13
|
+
}
|
|
14
|
+
function clampText(value, maxChars) {
|
|
15
|
+
if (!value)
|
|
16
|
+
return undefined;
|
|
17
|
+
if (value.length <= maxChars)
|
|
18
|
+
return value;
|
|
19
|
+
return `${value.slice(0, Math.max(0, maxChars - 1)).trimEnd()}…`;
|
|
20
|
+
}
|
|
21
|
+
function normalizeFailureSummary(value) {
|
|
22
|
+
if (!value)
|
|
23
|
+
return undefined;
|
|
24
|
+
const next = {};
|
|
25
|
+
const reasonCode = trimToUndefined(value.reasonCode);
|
|
26
|
+
if (reasonCode)
|
|
27
|
+
next.reasonCode = reasonCode;
|
|
28
|
+
const diagnosticCode = trimToUndefined(value.diagnosticCode);
|
|
29
|
+
if (diagnosticCode)
|
|
30
|
+
next.diagnosticCode = diagnosticCode;
|
|
31
|
+
const safeHint = clampText(trimToUndefined(value.safeHint), MAX_RECOVERY_SAFE_HINT_CHARS);
|
|
32
|
+
if (safeHint)
|
|
33
|
+
next.safeHint = safeHint;
|
|
34
|
+
const failurePhase = trimToUndefined(value.failurePhase);
|
|
35
|
+
if (failurePhase)
|
|
36
|
+
next.failurePhase = failurePhase;
|
|
37
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
38
|
+
}
|
|
39
|
+
function normalizeRecentReadFiles(value) {
|
|
40
|
+
if (!Array.isArray(value) || value.length === 0)
|
|
41
|
+
return undefined;
|
|
42
|
+
const unique = [];
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
for (const entry of value) {
|
|
45
|
+
const path = trimToUndefined(typeof entry === 'string' ? entry : entry?.path);
|
|
46
|
+
if (!path || seen.has(path))
|
|
47
|
+
continue;
|
|
48
|
+
seen.add(path);
|
|
49
|
+
unique.push(path);
|
|
50
|
+
}
|
|
51
|
+
if (unique.length === 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
return unique.slice(-MAX_RECOVERY_READ_FILES);
|
|
54
|
+
}
|
|
55
|
+
function buildRecoveryState(params) {
|
|
56
|
+
const { sessionManager, persistedState, patch } = params;
|
|
57
|
+
const next = {};
|
|
58
|
+
const flowMode = sessionManager.getChatFlowMode?.() ?? persistedState?.recoveryState?.flowMode;
|
|
59
|
+
if (flowMode) {
|
|
60
|
+
next.flowMode = flowMode;
|
|
61
|
+
}
|
|
62
|
+
const recentReadFiles = normalizeRecentReadFiles(sessionManager.getArtifactState?.()?.recentReadArtifacts ??
|
|
63
|
+
persistedState?.recoveryState?.recentReadFiles);
|
|
64
|
+
if (recentReadFiles?.length) {
|
|
65
|
+
next.recentReadFiles = recentReadFiles;
|
|
66
|
+
}
|
|
67
|
+
const lastFailureSummary = patch && 'lastFailureSummary' in patch
|
|
68
|
+
? normalizeFailureSummary(patch.lastFailureSummary)
|
|
69
|
+
: normalizeFailureSummary(persistedState?.recoveryState?.lastFailureSummary);
|
|
70
|
+
if (lastFailureSummary) {
|
|
71
|
+
next.lastFailureSummary = lastFailureSummary;
|
|
72
|
+
}
|
|
73
|
+
return Object.keys(next).length > 0 ? next : undefined;
|
|
74
|
+
}
|
|
75
|
+
function buildRecoveryStateMessage(summaryState) {
|
|
76
|
+
const recoveryState = summaryState?.recoveryState;
|
|
77
|
+
if (!recoveryState)
|
|
78
|
+
return undefined;
|
|
79
|
+
return {
|
|
80
|
+
role: 'system',
|
|
81
|
+
content: `[Conversation recovery state]\n${JSON.stringify(recoveryState)}`,
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function fitRecoveryStateMessage(params) {
|
|
85
|
+
const { message, budgetTokens, countTokens } = params;
|
|
86
|
+
if (!message)
|
|
87
|
+
return undefined;
|
|
88
|
+
if (budgetTokens <= 0)
|
|
89
|
+
return undefined;
|
|
90
|
+
const tokens = Math.max(0, Math.floor(countTokens(message.content)));
|
|
91
|
+
return tokens <= budgetTokens ? message : undefined;
|
|
92
|
+
}
|
|
4
93
|
export async function refreshSessionSummary(params) {
|
|
5
94
|
const { sessionManager, llm, contextHash } = params;
|
|
6
95
|
const strategy = params.strategy ?? 'auto';
|
|
7
96
|
if (!sessionManager)
|
|
8
|
-
return;
|
|
97
|
+
return { didSummarize: false };
|
|
9
98
|
try {
|
|
10
99
|
const summarizer = new ConversationSummarizer({
|
|
11
100
|
chat: async ({ messages, temperature, maxTokens }) => {
|
|
@@ -32,33 +121,58 @@ export async function refreshSessionSummary(params) {
|
|
|
32
121
|
if (persistedState) {
|
|
33
122
|
summarizer.restoreState(persistedState);
|
|
34
123
|
}
|
|
35
|
-
const
|
|
36
|
-
|
|
124
|
+
const rawMessages = sessionManager.getMessagesWithIds();
|
|
125
|
+
const messages = microcompact(rawMessages).map((msg, index) => ({
|
|
126
|
+
id: msg.id ?? rawMessages[index]?.id ?? `msg-${index}-${msg.timestamp}`,
|
|
37
127
|
role: msg.role,
|
|
38
128
|
content: msg.content,
|
|
39
129
|
timestamp: msg.timestamp,
|
|
40
130
|
}));
|
|
131
|
+
let didSummarize = false;
|
|
41
132
|
if (strategy === 'force') {
|
|
42
|
-
await summarizer.forceSummarize(messages, contextHash);
|
|
133
|
+
const result = await summarizer.forceSummarize(messages, contextHash);
|
|
134
|
+
didSummarize = Boolean(result);
|
|
43
135
|
}
|
|
44
136
|
else {
|
|
45
|
-
await summarizer.triggerSummarization(messages, contextHash);
|
|
137
|
+
const result = await summarizer.triggerSummarization(messages, contextHash);
|
|
138
|
+
didSummarize = Boolean(result);
|
|
46
139
|
}
|
|
47
|
-
|
|
140
|
+
const nextState = summarizer.getState();
|
|
141
|
+
nextState.recoveryState = buildRecoveryState({
|
|
142
|
+
sessionManager,
|
|
143
|
+
persistedState,
|
|
144
|
+
patch: params.recoveryStatePatch,
|
|
145
|
+
});
|
|
146
|
+
sessionManager.updateSummaryState(nextState);
|
|
147
|
+
return { didSummarize };
|
|
48
148
|
}
|
|
49
|
-
catch {
|
|
50
|
-
|
|
149
|
+
catch (error) {
|
|
150
|
+
if (params.strict) {
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
return { didSummarize: false, error: error instanceof Error ? error.message : String(error) };
|
|
51
154
|
}
|
|
52
155
|
}
|
|
53
156
|
export function buildEffectiveConversationContext(params) {
|
|
54
157
|
const { sessionManager } = params;
|
|
55
158
|
const summaryState = sessionManager.getSummaryState();
|
|
56
|
-
const
|
|
57
|
-
|
|
159
|
+
const countTokens = params.countTokens ?? ((text) => TokenTracker.estimateTokens(text));
|
|
160
|
+
// Apply microcompact (Level 0) to all messages before building context
|
|
161
|
+
// This is a "view-only" operation that doesn't modify sessionManager history
|
|
162
|
+
const rawMessages = params.messages ?? sessionManager.getMessages();
|
|
163
|
+
const messages = microcompact(rawMessages).map((msg, index) => ({
|
|
164
|
+
id: msg.id ?? `msg-${index}-${msg.timestamp}`,
|
|
58
165
|
role: msg.role,
|
|
59
166
|
content: msg.content,
|
|
60
167
|
timestamp: msg.timestamp,
|
|
61
168
|
}));
|
|
169
|
+
if (!summaryState) {
|
|
170
|
+
return buildSessionConversationContext(messages, {
|
|
171
|
+
budgetTokens: params.budgetTokens ?? Number.MAX_SAFE_INTEGER,
|
|
172
|
+
maxMessages: params.maxMessages,
|
|
173
|
+
countTokens,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
62
176
|
const summarizer = new ConversationSummarizer({
|
|
63
177
|
chat: async () => ({ content: '' }),
|
|
64
178
|
}, {
|
|
@@ -75,8 +189,45 @@ export function buildEffectiveConversationContext(params) {
|
|
|
75
189
|
if (summaryState) {
|
|
76
190
|
summarizer.restoreState(summaryState);
|
|
77
191
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
.
|
|
192
|
+
const effective = summarizer.getEffectiveContext(messages);
|
|
193
|
+
const recentMessages = effective
|
|
194
|
+
.filter((message) => message.role === 'user' || message.role === 'assistant')
|
|
195
|
+
.map((message) => ({
|
|
196
|
+
id: message.id,
|
|
197
|
+
role: message.role,
|
|
198
|
+
content: message.content,
|
|
199
|
+
timestamp: message.timestamp,
|
|
200
|
+
}));
|
|
201
|
+
const recoveryMessage = fitRecoveryStateMessage({
|
|
202
|
+
message: buildRecoveryStateMessage(summaryState),
|
|
203
|
+
budgetTokens: params.budgetTokens ?? Number.MAX_SAFE_INTEGER,
|
|
204
|
+
countTokens,
|
|
205
|
+
});
|
|
206
|
+
const recoveryBudget = recoveryMessage && params.budgetTokens !== undefined
|
|
207
|
+
? Math.max(0, params.budgetTokens - Math.max(0, Math.floor(countTokens(recoveryMessage.content))))
|
|
208
|
+
: params.budgetTokens;
|
|
209
|
+
const summaryStateForContext = summaryState.recoveryState === undefined
|
|
210
|
+
? summaryState
|
|
211
|
+
: {
|
|
212
|
+
...summaryState,
|
|
213
|
+
recoveryState: undefined,
|
|
214
|
+
};
|
|
215
|
+
const built = buildSessionConversationContext(recentMessages, {
|
|
216
|
+
budgetTokens: recoveryBudget ?? Number.MAX_SAFE_INTEGER,
|
|
217
|
+
maxMessages: params.maxMessages,
|
|
218
|
+
countTokens,
|
|
219
|
+
summaryState: summaryStateForContext,
|
|
220
|
+
});
|
|
221
|
+
if (!recoveryMessage)
|
|
222
|
+
return built;
|
|
223
|
+
let systemPrefixLength = 0;
|
|
224
|
+
while (systemPrefixLength < built.length && built[systemPrefixLength]?.role === 'system') {
|
|
225
|
+
systemPrefixLength += 1;
|
|
226
|
+
}
|
|
227
|
+
return [
|
|
228
|
+
...built.slice(0, systemPrefixLength),
|
|
229
|
+
recoveryMessage,
|
|
230
|
+
...built.slice(systemPrefixLength),
|
|
231
|
+
];
|
|
81
232
|
}
|
|
82
233
|
//# sourceMappingURL=summary-sync.js.map
|
|
@@ -78,5 +78,11 @@ export class TokenTracker {
|
|
|
78
78
|
static estimateTokens(text) {
|
|
79
79
|
return Math.ceil(text.length / 4);
|
|
80
80
|
}
|
|
81
|
+
/**
|
|
82
|
+
* Estimate total tokens for a list of messages
|
|
83
|
+
*/
|
|
84
|
+
static estimateMessagesTokens(messages) {
|
|
85
|
+
return messages.reduce((sum, m) => sum + this.estimateTokens(m.content), 0);
|
|
86
|
+
}
|
|
81
87
|
}
|
|
82
88
|
//# sourceMappingURL=token-tracker.js.map
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import { tryGetLogger } from '../observability/logger.js';
|
|
3
|
+
/**
|
|
4
|
+
* Compute a stable SHA-256 hash (truncated to 16 hex chars) of the given arguments text.
|
|
5
|
+
*/
|
|
6
|
+
export function hashSkillArgs(argsText) {
|
|
7
|
+
if (!argsText)
|
|
8
|
+
return undefined;
|
|
9
|
+
return crypto.createHash('sha256').update(argsText).digest('hex').slice(0, 16);
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Generate a unique trace ID for a skill execution.
|
|
13
|
+
*/
|
|
14
|
+
export function generateSkillTraceId(skillId) {
|
|
15
|
+
return `skill-${skillId}-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Emit a structured skill audit event via the logger audit trail.
|
|
19
|
+
*
|
|
20
|
+
* Uses `logger.audit()` to ensure events are persisted in the audit trail
|
|
21
|
+
* with appropriate severity and source metadata.
|
|
22
|
+
*/
|
|
23
|
+
export function emitSkillAuditEvent(event) {
|
|
24
|
+
const logger = tryGetLogger();
|
|
25
|
+
if (!logger)
|
|
26
|
+
return;
|
|
27
|
+
const severity = event.type === 'SKILL_EXECUTION_DENIED' ? 'high' : 'low';
|
|
28
|
+
logger.audit(event.type, event, {
|
|
29
|
+
source: 'skill-executor',
|
|
30
|
+
severity,
|
|
31
|
+
scope: 'session',
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=audit.js.map
|