@flyingrobots/graft 0.7.0 → 0.7.1
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/CHANGELOG.md +13 -0
- package/README.md +0 -1
- package/bin/graft.js +26 -26
- package/dist/cli/init-client-config.d.ts.map +1 -1
- package/dist/cli/init-client-config.js +40 -4
- package/dist/cli/init-client-config.js.map +1 -1
- package/dist/cli/init-model.js +2 -2
- package/dist/cli/init-model.js.map +1 -1
- package/dist/cli/json-document.d.ts +1 -0
- package/dist/cli/json-document.d.ts.map +1 -1
- package/dist/cli/json-document.js +3 -0
- package/dist/cli/json-document.js.map +1 -1
- package/dist/hooks/posttooluse-read.js +1 -1
- package/dist/hooks/posttooluse-read.js.map +1 -1
- package/dist/hooks/pretooluse-read.js +1 -1
- package/dist/hooks/pretooluse-read.js.map +1 -1
- package/dist/mcp/daemon-worker-child-pool.d.ts +2 -0
- package/dist/mcp/daemon-worker-child-pool.d.ts.map +1 -1
- package/dist/mcp/daemon-worker-child-pool.js +10 -3
- package/dist/mcp/daemon-worker-child-pool.js.map +1 -1
- package/dist/mcp/daemon-worker-pool.d.ts +1 -1
- package/dist/mcp/daemon-worker-pool.d.ts.map +1 -1
- package/dist/mcp/daemon-worker-pool.js +1 -1
- package/dist/mcp/daemon-worker-pool.js.map +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +3 -1
- package/dist/version.js.map +1 -1
- package/docs/SETUP.md +11 -7
- package/package.json +9 -4
- package/src/adapters/canonical-json.ts +0 -56
- package/src/adapters/json-text-decoder.ts +0 -11
- package/src/adapters/node-fs.ts +0 -43
- package/src/adapters/node-git.ts +0 -47
- package/src/adapters/node-paths.ts +0 -35
- package/src/adapters/node-process-runner.ts +0 -27
- package/src/adapters/repo-paths.ts +0 -63
- package/src/adapters/rotating-ndjson-log.ts +0 -66
- package/src/api/index.ts +0 -107
- package/src/api/repo-local-graft.ts +0 -21
- package/src/api/repo-workspace.ts +0 -34
- package/src/api/tool-bridge.ts +0 -22
- package/src/cli/activity-render.ts +0 -112
- package/src/cli/cli-error.ts +0 -192
- package/src/cli/command-parser.ts +0 -484
- package/src/cli/daemon-status-model.ts +0 -238
- package/src/cli/daemon-status-render.ts +0 -102
- package/src/cli/daemon-status.ts +0 -100
- package/src/cli/entrypoint.ts +0 -18
- package/src/cli/git-graft-enhance-model.ts +0 -131
- package/src/cli/git-graft-enhance-render.ts +0 -33
- package/src/cli/git-graft-enhance.ts +0 -54
- package/src/cli/index-cmd.ts +0 -90
- package/src/cli/index-model.ts +0 -75
- package/src/cli/init-bootstrap.ts +0 -47
- package/src/cli/init-client-config.ts +0 -428
- package/src/cli/init-model.ts +0 -284
- package/src/cli/init-render.ts +0 -54
- package/src/cli/init-target-hooks.ts +0 -93
- package/src/cli/init.ts +0 -124
- package/src/cli/json-document.ts +0 -155
- package/src/cli/local-history-dag-model.ts +0 -437
- package/src/cli/local-history-dag-render.ts +0 -133
- package/src/cli/local-history-dag.ts +0 -103
- package/src/cli/main.ts +0 -197
- package/src/cli/migrate-local-history.ts +0 -119
- package/src/cli/peer-command.ts +0 -60
- package/src/contracts/capabilities.ts +0 -540
- package/src/contracts/causal-ontology.ts +0 -627
- package/src/contracts/causal-surface-next-action.ts +0 -18
- package/src/contracts/json-object.ts +0 -18
- package/src/contracts/mcp-runtime.ts +0 -16
- package/src/contracts/output-schema-cli.ts +0 -103
- package/src/contracts/output-schema-fragments.ts +0 -881
- package/src/contracts/output-schema-mcp.ts +0 -501
- package/src/contracts/output-schema-meta.ts +0 -119
- package/src/contracts/output-schemas.ts +0 -1524
- package/src/git/diff.ts +0 -161
- package/src/git/target-git-hook-bootstrap.ts +0 -56
- package/src/guards/stream-boundary.ts +0 -110
- package/src/hooks/posttooluse-read.ts +0 -54
- package/src/hooks/pretooluse-read.ts +0 -52
- package/src/hooks/read-governor.ts +0 -95
- package/src/hooks/read-messages.ts +0 -53
- package/src/hooks/shared.ts +0 -168
- package/src/index.ts +0 -1
- package/src/mcp/burden.ts +0 -132
- package/src/mcp/cache.ts +0 -7
- package/src/mcp/cached-file.ts +0 -1
- package/src/mcp/context.ts +0 -150
- package/src/mcp/control-plane/authz-storage.ts +0 -129
- package/src/mcp/control-plane/session-registry.ts +0 -115
- package/src/mcp/control-plane/status-projection.ts +0 -76
- package/src/mcp/control-plane/types.ts +0 -144
- package/src/mcp/daemon-bootstrap.ts +0 -100
- package/src/mcp/daemon-control-plane.ts +0 -310
- package/src/mcp/daemon-job-scheduler.ts +0 -279
- package/src/mcp/daemon-repos.ts +0 -99
- package/src/mcp/daemon-server.ts +0 -172
- package/src/mcp/daemon-session-host.ts +0 -234
- package/src/mcp/daemon-stdio-bridge.ts +0 -262
- package/src/mcp/daemon-worker-child-pool.ts +0 -213
- package/src/mcp/daemon-worker-inline-pool.ts +0 -46
- package/src/mcp/daemon-worker-pool.ts +0 -7
- package/src/mcp/daemon-worker-process.ts +0 -52
- package/src/mcp/daemon-worker-types.ts +0 -66
- package/src/mcp/metrics.ts +0 -176
- package/src/mcp/monitor-health.ts +0 -83
- package/src/mcp/monitor-persistence.ts +0 -54
- package/src/mcp/monitor-tick-job.ts +0 -153
- package/src/mcp/monitor-types.ts +0 -103
- package/src/mcp/persisted-local-history-graph.ts +0 -592
- package/src/mcp/persisted-local-history-policy.ts +0 -501
- package/src/mcp/persisted-local-history-views.ts +0 -250
- package/src/mcp/persisted-local-history.ts +0 -1621
- package/src/mcp/persistent-monitor-runtime.ts +0 -482
- package/src/mcp/policy.ts +0 -75
- package/src/mcp/receipt.ts +0 -201
- package/src/mcp/repo-concurrency.ts +0 -392
- package/src/mcp/repo-overview/filter.ts +0 -44
- package/src/mcp/repo-overview/types.ts +0 -63
- package/src/mcp/repo-overview/view-projection.ts +0 -90
- package/src/mcp/repo-state-git.ts +0 -247
- package/src/mcp/repo-state-observation.ts +0 -203
- package/src/mcp/repo-state-transition.ts +0 -151
- package/src/mcp/repo-state-types.ts +0 -107
- package/src/mcp/repo-state.ts +0 -146
- package/src/mcp/repo-tool-job.ts +0 -102
- package/src/mcp/repo-tool-worker-context.ts +0 -215
- package/src/mcp/repo-workspace.ts +0 -21
- package/src/mcp/run-capture-config.ts +0 -33
- package/src/mcp/runtime-causal-context.ts +0 -72
- package/src/mcp/runtime-observability.ts +0 -217
- package/src/mcp/runtime-staged-target.ts +0 -161
- package/src/mcp/runtime-workspace-overlay.ts +0 -255
- package/src/mcp/secret-scrub.ts +0 -89
- package/src/mcp/semantic-transition-guidance.ts +0 -60
- package/src/mcp/semantic-transition-summary.ts +0 -130
- package/src/mcp/server-context.ts +0 -236
- package/src/mcp/server-invocation.ts +0 -388
- package/src/mcp/server-tool-access.ts +0 -148
- package/src/mcp/server.ts +0 -270
- package/src/mcp/stdio-server.ts +0 -26
- package/src/mcp/stdio.ts +0 -3
- package/src/mcp/tool-registry.ts +0 -98
- package/src/mcp/tools/activity-view.ts +0 -325
- package/src/mcp/tools/budget.ts +0 -23
- package/src/mcp/tools/causal-attach.ts +0 -67
- package/src/mcp/tools/causal-status.ts +0 -58
- package/src/mcp/tools/changed-since.ts +0 -71
- package/src/mcp/tools/code-find.ts +0 -192
- package/src/mcp/tools/code-refs.ts +0 -470
- package/src/mcp/tools/code-show.ts +0 -289
- package/src/mcp/tools/daemon-monitors.ts +0 -14
- package/src/mcp/tools/daemon-repos.ts +0 -22
- package/src/mcp/tools/daemon-sessions.ts +0 -14
- package/src/mcp/tools/daemon-status.ts +0 -12
- package/src/mcp/tools/diagnostic-models.ts +0 -97
- package/src/mcp/tools/doctor.ts +0 -83
- package/src/mcp/tools/explain.ts +0 -84
- package/src/mcp/tools/export-surface-diff.ts +0 -26
- package/src/mcp/tools/file-outline.ts +0 -67
- package/src/mcp/tools/git-files.ts +0 -73
- package/src/mcp/tools/graft-diff.ts +0 -35
- package/src/mcp/tools/graft-edit.ts +0 -276
- package/src/mcp/tools/knowledge-map.ts +0 -34
- package/src/mcp/tools/map-collector.ts +0 -338
- package/src/mcp/tools/map.ts +0 -185
- package/src/mcp/tools/monitor-nudge.ts +0 -19
- package/src/mcp/tools/monitor-pause.ts +0 -18
- package/src/mcp/tools/monitor-resume.ts +0 -18
- package/src/mcp/tools/monitor-start.ts +0 -20
- package/src/mcp/tools/monitor-stop.ts +0 -18
- package/src/mcp/tools/precision-live.ts +0 -158
- package/src/mcp/tools/precision-match.ts +0 -54
- package/src/mcp/tools/precision-paths.ts +0 -59
- package/src/mcp/tools/precision-query.ts +0 -127
- package/src/mcp/tools/precision-show.ts +0 -86
- package/src/mcp/tools/precision-visibility.ts +0 -44
- package/src/mcp/tools/precision-warp.ts +0 -101
- package/src/mcp/tools/precision.ts +0 -21
- package/src/mcp/tools/read-range.ts +0 -28
- package/src/mcp/tools/refactor-difficulty.ts +0 -42
- package/src/mcp/tools/run-capture.ts +0 -149
- package/src/mcp/tools/safe-read.ts +0 -148
- package/src/mcp/tools/since.ts +0 -53
- package/src/mcp/tools/state.ts +0 -39
- package/src/mcp/tools/stats.ts +0 -24
- package/src/mcp/tools/structural-blame.ts +0 -55
- package/src/mcp/tools/structural-churn.ts +0 -28
- package/src/mcp/tools/structural-log.ts +0 -41
- package/src/mcp/tools/structural-review.ts +0 -63
- package/src/mcp/tools/workspace-authorizations.ts +0 -14
- package/src/mcp/tools/workspace-authorize.ts +0 -20
- package/src/mcp/tools/workspace-bind.ts +0 -25
- package/src/mcp/tools/workspace-rebind.ts +0 -25
- package/src/mcp/tools/workspace-revoke.ts +0 -18
- package/src/mcp/tools/workspace-status.ts +0 -12
- package/src/mcp/warp-pool.ts +0 -36
- package/src/mcp/workspace-read-observation-model.ts +0 -48
- package/src/mcp/workspace-read-observation.ts +0 -103
- package/src/mcp/workspace-router-capability.ts +0 -32
- package/src/mcp/workspace-router-history.ts +0 -233
- package/src/mcp/workspace-router-model.ts +0 -131
- package/src/mcp/workspace-router-resolution.ts +0 -71
- package/src/mcp/workspace-router-runtime.ts +0 -246
- package/src/mcp/workspace-router.ts +0 -688
- package/src/metrics/logger.ts +0 -40
- package/src/metrics/types.ts +0 -40
- package/src/operations/adaptive-projection.ts +0 -54
- package/src/operations/agent-handoff.ts +0 -59
- package/src/operations/cached-file.ts +0 -40
- package/src/operations/capture-range.ts +0 -64
- package/src/operations/conversation-primer.ts +0 -82
- package/src/operations/cross-session-resume.ts +0 -104
- package/src/operations/deterministic-replay.ts +0 -143
- package/src/operations/diff-identity.ts +0 -127
- package/src/operations/export-surface-diff.ts +0 -355
- package/src/operations/file-outline.ts +0 -48
- package/src/operations/footprint-parallelism.ts +0 -98
- package/src/operations/graft-diff.ts +0 -169
- package/src/operations/horizon-of-readability.ts +0 -60
- package/src/operations/import-reference-impact.ts +0 -143
- package/src/operations/knowledge-map.ts +0 -96
- package/src/operations/observation-cache.ts +0 -165
- package/src/operations/projection-safety.ts +0 -111
- package/src/operations/read-range.ts +0 -64
- package/src/operations/repo-workspace.ts +0 -339
- package/src/operations/result-dto.ts +0 -23
- package/src/operations/safe-read.ts +0 -118
- package/src/operations/semantic-drift.ts +0 -70
- package/src/operations/session-filtration.ts +0 -76
- package/src/operations/session-replay.ts +0 -73
- package/src/operations/sludge-detector.ts +0 -356
- package/src/operations/state.ts +0 -66
- package/src/operations/structural-blame.ts +0 -197
- package/src/operations/structural-review.ts +0 -317
- package/src/operations/structured-buffer-compare.ts +0 -429
- package/src/operations/structured-buffer-model.ts +0 -456
- package/src/operations/structured-buffer-query.ts +0 -794
- package/src/operations/structured-buffer.ts +0 -181
- package/src/operations/teaching-hints.ts +0 -52
- package/src/parser/diff.ts +0 -320
- package/src/parser/lang.ts +0 -71
- package/src/parser/markdown.ts +0 -197
- package/src/parser/outline.ts +0 -316
- package/src/parser/runtime.ts +0 -71
- package/src/parser/types.ts +0 -75
- package/src/policy/evaluate.ts +0 -178
- package/src/policy/graftignore.ts +0 -6
- package/src/policy/types.ts +0 -87
- package/src/ports/codec.ts +0 -13
- package/src/ports/filesystem.ts +0 -18
- package/src/ports/git.ts +0 -16
- package/src/ports/guards.ts +0 -68
- package/src/ports/paths.ts +0 -22
- package/src/ports/process-runner.ts +0 -22
- package/src/release/security-gate.ts +0 -102
- package/src/session/tracker.ts +0 -145
- package/src/session/types.ts +0 -23
- package/src/version.ts +0 -3
- package/src/warp/ast-emitter.ts +0 -133
- package/src/warp/ast-import-resolver.ts +0 -333
- package/src/warp/commit-meta.ts +0 -19
- package/src/warp/context.ts +0 -48
- package/src/warp/dead-symbols.ts +0 -174
- package/src/warp/drift-sentinel.ts +0 -101
- package/src/warp/index-head.ts +0 -348
- package/src/warp/observers.ts +0 -101
- package/src/warp/open.ts +0 -67
- package/src/warp/outline-diff-trailer.ts +0 -100
- package/src/warp/plumbing.d.ts +0 -24
- package/src/warp/refactor-difficulty.ts +0 -257
- package/src/warp/references.ts +0 -77
- package/src/warp/stale-docs.ts +0 -210
- package/src/warp/structural-drift-detection.ts +0 -97
- package/src/warp/structural-queries.ts +0 -258
- package/src/warp/symbol-timeline.ts +0 -209
- package/src/warp/traverse-hydrate.ts +0 -61
- package/src/warp/warp-reference-count.ts +0 -47
- package/src/warp/warp-structural-blame.ts +0 -65
- package/src/warp/warp-structural-churn.ts +0 -309
- package/src/warp/warp-structural-log.ts +0 -149
- package/src/warp/writer-id.ts +0 -30
package/src/metrics/logger.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { RotatingNdjsonLog } from "../adapters/rotating-ndjson-log.js";
|
|
2
|
-
import type { FileSystem } from "../ports/filesystem.js";
|
|
3
|
-
import type { JsonCodec } from "../ports/codec.js";
|
|
4
|
-
import { DecisionEntry } from "./types.js";
|
|
5
|
-
|
|
6
|
-
export interface MetricsLoggerOptions {
|
|
7
|
-
readonly fs: FileSystem;
|
|
8
|
-
readonly codec: JsonCodec;
|
|
9
|
-
readonly maxBytes?: number;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export class MetricsLogger {
|
|
13
|
-
private readonly writer: RotatingNdjsonLog;
|
|
14
|
-
|
|
15
|
-
constructor(logPath: string, options: MetricsLoggerOptions) {
|
|
16
|
-
const writerOptions = {
|
|
17
|
-
logPath,
|
|
18
|
-
fs: options.fs,
|
|
19
|
-
codec: options.codec,
|
|
20
|
-
...(options.maxBytes === undefined ? {} : { maxBytes: options.maxBytes }),
|
|
21
|
-
};
|
|
22
|
-
this.writer = new RotatingNdjsonLog(writerOptions);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
async log(entry: Omit<DecisionEntry, "ts">): Promise<void> {
|
|
26
|
-
const full = new DecisionEntry({
|
|
27
|
-
ts: new Date().toISOString(),
|
|
28
|
-
command: entry.command,
|
|
29
|
-
path: entry.path,
|
|
30
|
-
projection: entry.projection,
|
|
31
|
-
reason: entry.reason,
|
|
32
|
-
lines: entry.lines,
|
|
33
|
-
bytes: entry.bytes,
|
|
34
|
-
estimatedBytesAvoided: entry.estimatedBytesAvoided,
|
|
35
|
-
sessionDepth: entry.sessionDepth,
|
|
36
|
-
tripwire: entry.tripwire,
|
|
37
|
-
});
|
|
38
|
-
await this.writer.append(full);
|
|
39
|
-
}
|
|
40
|
-
}
|
package/src/metrics/types.ts
DELETED
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// DecisionEntry — runtime-backed metric event for policy decisions
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
export class DecisionEntry {
|
|
6
|
-
readonly ts: string;
|
|
7
|
-
readonly command: string;
|
|
8
|
-
readonly path: string;
|
|
9
|
-
readonly projection: string;
|
|
10
|
-
readonly reason: string;
|
|
11
|
-
readonly lines: number;
|
|
12
|
-
readonly bytes: number;
|
|
13
|
-
readonly estimatedBytesAvoided?: number | undefined;
|
|
14
|
-
readonly sessionDepth?: string | undefined;
|
|
15
|
-
readonly tripwire?: string | null | undefined;
|
|
16
|
-
|
|
17
|
-
constructor(fields: {
|
|
18
|
-
readonly ts: string;
|
|
19
|
-
readonly command: string;
|
|
20
|
-
readonly path: string;
|
|
21
|
-
readonly projection: string;
|
|
22
|
-
readonly reason: string;
|
|
23
|
-
readonly lines: number;
|
|
24
|
-
readonly bytes: number;
|
|
25
|
-
readonly estimatedBytesAvoided?: number | undefined;
|
|
26
|
-
readonly sessionDepth?: string | undefined;
|
|
27
|
-
readonly tripwire?: string | null | undefined;
|
|
28
|
-
}) {
|
|
29
|
-
this.ts = fields.ts;
|
|
30
|
-
this.command = fields.command;
|
|
31
|
-
this.path = fields.path;
|
|
32
|
-
this.projection = fields.projection;
|
|
33
|
-
this.reason = fields.reason;
|
|
34
|
-
this.lines = fields.lines;
|
|
35
|
-
this.bytes = fields.bytes;
|
|
36
|
-
this.estimatedBytesAvoided = fields.estimatedBytesAvoided;
|
|
37
|
-
this.sessionDepth = fields.sessionDepth;
|
|
38
|
-
this.tripwire = fields.tripwire;
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Adaptive Projection Selection — structural auto-selection
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
/** Structural metrics for projection selection. */
|
|
6
|
-
export interface ProjectionCandidate {
|
|
7
|
-
readonly fileLines: number;
|
|
8
|
-
readonly fileBytes: number;
|
|
9
|
-
readonly symbolCount: number;
|
|
10
|
-
readonly outlineCompressionRatio: number;
|
|
11
|
-
readonly targetSymbol?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** Selected projection with reasoning. */
|
|
15
|
-
export interface ProjectionSelection {
|
|
16
|
-
readonly projection: "outline" | "content" | "range";
|
|
17
|
-
readonly reason: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
const SMALL_FILE_THRESHOLD = 50;
|
|
21
|
-
const HORIZON_RATIO = 0.7;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Select the optimal projection based on structural metrics.
|
|
25
|
-
*
|
|
26
|
-
* - Dense large files → outline (saves the most context)
|
|
27
|
-
* - Small or irreducibly complex files → content (outline doesn't help)
|
|
28
|
-
* - Targeted symbol investigation → range (surgical read)
|
|
29
|
-
*/
|
|
30
|
-
export function selectProjection(candidate: ProjectionCandidate): ProjectionSelection {
|
|
31
|
-
// If a specific symbol is targeted, use range
|
|
32
|
-
if (candidate.targetSymbol !== undefined) {
|
|
33
|
-
return {
|
|
34
|
-
projection: "range",
|
|
35
|
-
reason: `Targeted read for symbol "${candidate.targetSymbol}" — range is most efficient.`,
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// Small files or high compression ratio → content
|
|
40
|
-
if (candidate.fileLines <= SMALL_FILE_THRESHOLD || candidate.outlineCompressionRatio >= HORIZON_RATIO) {
|
|
41
|
-
return {
|
|
42
|
-
projection: "content",
|
|
43
|
-
reason: candidate.fileLines <= SMALL_FILE_THRESHOLD
|
|
44
|
-
? `Small file (${String(candidate.fileLines)} lines) — content fits easily.`
|
|
45
|
-
: `Outline is ${String(Math.round(candidate.outlineCompressionRatio * 100))}% of content — outline provides negligible savings.`,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
// Default: outline for larger files with good compression
|
|
50
|
-
return {
|
|
51
|
-
projection: "outline",
|
|
52
|
-
reason: `${String(candidate.fileLines)} lines, ${String(candidate.symbolCount)} symbols, ${String(Math.round(candidate.outlineCompressionRatio * 100))}% compression — outline is efficient.`,
|
|
53
|
-
};
|
|
54
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Agent Handoff Protocol — structured session transfer
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { ObservationCache } from "./observation-cache.js";
|
|
6
|
-
|
|
7
|
-
/** The structured handoff payload. */
|
|
8
|
-
export interface HandoffPayload {
|
|
9
|
-
readonly sessionId: string;
|
|
10
|
-
readonly filesRead: readonly string[];
|
|
11
|
-
readonly symbolsInspected: readonly string[];
|
|
12
|
-
readonly observations: number;
|
|
13
|
-
readonly budgetConsumed: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/** Options for building a handoff. */
|
|
17
|
-
export interface HandoffOptions {
|
|
18
|
-
readonly cache: ObservationCache;
|
|
19
|
-
readonly sessionId: string;
|
|
20
|
-
readonly budgetTotal?: number;
|
|
21
|
-
readonly budgetConsumed?: number;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Build a structured handoff payload from the current session state.
|
|
26
|
-
*
|
|
27
|
-
* Projects the observation cache into a portable JSON payload that
|
|
28
|
-
* a receiving agent can use to understand what the departing agent
|
|
29
|
-
* knew and where it left off.
|
|
30
|
-
*/
|
|
31
|
-
export function buildHandoff(options: HandoffOptions): HandoffPayload {
|
|
32
|
-
const { cache, sessionId } = options;
|
|
33
|
-
|
|
34
|
-
const filesRead: string[] = [];
|
|
35
|
-
const symbolsInspected: string[] = [];
|
|
36
|
-
let observationCount = 0;
|
|
37
|
-
|
|
38
|
-
for (const [filePath, observation] of cache.allEntries()) {
|
|
39
|
-
filesRead.push(filePath);
|
|
40
|
-
for (const entry of observation.outline) {
|
|
41
|
-
symbolsInspected.push(entry.name);
|
|
42
|
-
}
|
|
43
|
-
observationCount++;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
let budgetStr = "unknown";
|
|
47
|
-
if (options.budgetTotal !== undefined && options.budgetConsumed !== undefined && options.budgetTotal > 0) {
|
|
48
|
-
const pct = Math.round((options.budgetConsumed / options.budgetTotal) * 100);
|
|
49
|
-
budgetStr = `${String(pct)}%`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
sessionId,
|
|
54
|
-
filesRead,
|
|
55
|
-
symbolsInspected,
|
|
56
|
-
observations: observationCount,
|
|
57
|
-
budgetConsumed: budgetStr,
|
|
58
|
-
};
|
|
59
|
-
}
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
import { extractOutlineForFile } from "../parser/outline.js";
|
|
2
|
-
import { detectStructuredFormat } from "../parser/lang.js";
|
|
3
|
-
import type { SupportedStructuredFormat } from "../parser/lang.js";
|
|
4
|
-
import type { OutlineEntry, JumpEntry } from "../parser/types.js";
|
|
5
|
-
import { hashContent } from "./observation-cache.js";
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Immutable snapshot of a file read. Built once from a single read,
|
|
9
|
-
* shared by all consumers (cache, policy, outline extraction) to eliminate
|
|
10
|
-
* TOCTOU races where the file changes between reads.
|
|
11
|
-
*/
|
|
12
|
-
export class CachedFile {
|
|
13
|
-
/** @internal */
|
|
14
|
-
private readonly _brand = "CachedFile" as const;
|
|
15
|
-
readonly path: string;
|
|
16
|
-
readonly rawContent: string;
|
|
17
|
-
readonly hash: string;
|
|
18
|
-
readonly lang: SupportedStructuredFormat | null;
|
|
19
|
-
readonly supportsOutline: boolean;
|
|
20
|
-
readonly outline: readonly OutlineEntry[];
|
|
21
|
-
readonly jumpTable: readonly JumpEntry[];
|
|
22
|
-
readonly actual: { readonly lines: number; readonly bytes: number };
|
|
23
|
-
|
|
24
|
-
constructor(filePath: string, rawContent: string) {
|
|
25
|
-
this.path = filePath;
|
|
26
|
-
this.rawContent = rawContent;
|
|
27
|
-
this.hash = hashContent(rawContent);
|
|
28
|
-
this.actual = {
|
|
29
|
-
lines: rawContent.split("\n").length,
|
|
30
|
-
bytes: Buffer.byteLength(rawContent),
|
|
31
|
-
};
|
|
32
|
-
this.lang = detectStructuredFormat(filePath);
|
|
33
|
-
this.supportsOutline = this.lang !== null;
|
|
34
|
-
const result = extractOutlineForFile(filePath, rawContent);
|
|
35
|
-
this.outline = result?.entries ?? [];
|
|
36
|
-
this.jumpTable = result?.jumpTable ?? [];
|
|
37
|
-
Object.freeze(this.actual);
|
|
38
|
-
Object.freeze(this);
|
|
39
|
-
}
|
|
40
|
-
}
|
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Capture Range — opaque handles for run_capture output
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
interface CaptureEntry {
|
|
8
|
-
readonly sessionId: string;
|
|
9
|
-
readonly content: string;
|
|
10
|
-
readonly lines: readonly string[];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Result of a range retrieval. */
|
|
14
|
-
export type CaptureRangeResult =
|
|
15
|
-
| { readonly ok: true; readonly lines: readonly string[]; readonly totalLines: number }
|
|
16
|
-
| { readonly ok: false; readonly error: string };
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Registry of opaque capture handles.
|
|
20
|
-
*
|
|
21
|
-
* Maps UUIDs to capture content. Handles are session-scoped — a
|
|
22
|
-
* handle from one session is rejected in another.
|
|
23
|
-
*/
|
|
24
|
-
export class CaptureHandleRegistry {
|
|
25
|
-
private nextId = 0;
|
|
26
|
-
private readonly entries = new Map<string, CaptureEntry>();
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Register capture content and return an opaque handle.
|
|
30
|
-
*/
|
|
31
|
-
register(sessionId: string, content: string): string {
|
|
32
|
-
const handle = `capture-${String(++this.nextId)}`;
|
|
33
|
-
const lines = content.split("\n").filter(
|
|
34
|
-
(line, index, arr) => index < arr.length - 1 || line.length > 0,
|
|
35
|
-
);
|
|
36
|
-
this.entries.set(handle, { sessionId, content, lines });
|
|
37
|
-
return handle;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Retrieve a line range from a capture handle.
|
|
42
|
-
*
|
|
43
|
-
* @param handle - The opaque handle from register()
|
|
44
|
-
* @param sessionId - The requesting session (must match the registering session)
|
|
45
|
-
* @param start - 1-based start line (inclusive)
|
|
46
|
-
* @param end - 1-based end line (inclusive)
|
|
47
|
-
*/
|
|
48
|
-
getRange(handle: string, sessionId: string, start: number, end: number): CaptureRangeResult {
|
|
49
|
-
const entry = this.entries.get(handle);
|
|
50
|
-
if (entry === undefined) {
|
|
51
|
-
return { ok: false, error: "Capture handle not found" };
|
|
52
|
-
}
|
|
53
|
-
if (entry.sessionId !== sessionId) {
|
|
54
|
-
return { ok: false, error: "Capture handle belongs to a different session" };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
const totalLines = entry.lines.length;
|
|
58
|
-
const clampedStart = Math.max(1, start);
|
|
59
|
-
const clampedEnd = Math.min(totalLines, end);
|
|
60
|
-
const lines = entry.lines.slice(clampedStart - 1, clampedEnd);
|
|
61
|
-
|
|
62
|
-
return { ok: true, lines, totalLines };
|
|
63
|
-
}
|
|
64
|
-
}
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Conversation Primer — auto-orient agent at session start via graft_map
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { GitClient } from "../ports/git.js";
|
|
6
|
-
import type { FileSystem } from "../ports/filesystem.js";
|
|
7
|
-
import type { PathOps } from "../ports/paths.js";
|
|
8
|
-
|
|
9
|
-
/** Result of building a conversation primer. */
|
|
10
|
-
export interface ConversationPrimerResult {
|
|
11
|
-
readonly scope: string;
|
|
12
|
-
readonly files: readonly string[];
|
|
13
|
-
readonly truncated: boolean;
|
|
14
|
-
readonly totalFiles: number;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
/** Options for building a primer. */
|
|
18
|
-
export interface ConversationPrimerOptions {
|
|
19
|
-
readonly cwd: string;
|
|
20
|
-
readonly git: GitClient;
|
|
21
|
-
readonly fs: FileSystem;
|
|
22
|
-
readonly pathOps: PathOps;
|
|
23
|
-
readonly scope?: string;
|
|
24
|
-
readonly maxFiles?: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Common source directory names, in priority order. */
|
|
28
|
-
const COMMON_SCOPES = ["src", "lib", "app", "packages"];
|
|
29
|
-
|
|
30
|
-
async function isDirectory(fileSystem: FileSystem, dirPath: string): Promise<boolean> {
|
|
31
|
-
try {
|
|
32
|
-
await fileSystem.readdir(dirPath);
|
|
33
|
-
return true;
|
|
34
|
-
} catch {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Build a conversation primer for the given project.
|
|
41
|
-
*
|
|
42
|
-
* Auto-detects the best scope (src/, lib/, etc.) or uses an explicit
|
|
43
|
-
* scope. Returns a list of tracked files, truncated if beyond maxFiles.
|
|
44
|
-
*/
|
|
45
|
-
export async function buildConversationPrimer(
|
|
46
|
-
options: ConversationPrimerOptions,
|
|
47
|
-
): Promise<ConversationPrimerResult> {
|
|
48
|
-
const { cwd, git, fs: fileSystem, pathOps, maxFiles = 50 } = options;
|
|
49
|
-
|
|
50
|
-
// Detect scope
|
|
51
|
-
let scope = options.scope ?? ".";
|
|
52
|
-
if (options.scope === undefined) {
|
|
53
|
-
for (const candidate of COMMON_SCOPES) {
|
|
54
|
-
const candidatePath = pathOps.join(cwd, candidate);
|
|
55
|
-
if (await isDirectory(fileSystem, candidatePath)) {
|
|
56
|
-
scope = candidate;
|
|
57
|
-
break;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// List tracked files via git ls-files (allowed by plumbing)
|
|
63
|
-
const lsResult = await git.run({
|
|
64
|
-
cwd,
|
|
65
|
-
args: ["ls-files", "--", scope],
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
const allFiles = lsResult.stdout
|
|
69
|
-
.split("\n")
|
|
70
|
-
.map((f) => f.trim())
|
|
71
|
-
.filter((f) => f.length > 0);
|
|
72
|
-
|
|
73
|
-
const truncated = allFiles.length > maxFiles;
|
|
74
|
-
const files = truncated ? allFiles.slice(0, maxFiles) : allFiles;
|
|
75
|
-
|
|
76
|
-
return {
|
|
77
|
-
scope,
|
|
78
|
-
files,
|
|
79
|
-
truncated,
|
|
80
|
-
totalFiles: allFiles.length,
|
|
81
|
-
};
|
|
82
|
-
}
|
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Cross-session resume — structural diff from saved HEAD to current HEAD
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { GitClient } from "../ports/git.js";
|
|
6
|
-
|
|
7
|
-
/** A file that changed between saved HEAD and current HEAD. */
|
|
8
|
-
export interface ResumeChangedFile {
|
|
9
|
-
readonly path: string;
|
|
10
|
-
readonly status: "added" | "modified" | "deleted" | "renamed";
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
/** Result of attempting to resume from a saved session state. */
|
|
14
|
-
export interface SessionResumeResult {
|
|
15
|
-
readonly resumable: boolean;
|
|
16
|
-
readonly currentHead: string;
|
|
17
|
-
readonly savedHead: string;
|
|
18
|
-
readonly changedFiles: readonly ResumeChangedFile[];
|
|
19
|
-
readonly commitsBehind: number;
|
|
20
|
-
readonly error?: string;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface SessionResumeOptions {
|
|
24
|
-
readonly cwd: string;
|
|
25
|
-
readonly savedHeadSha: string;
|
|
26
|
-
readonly git: GitClient;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Build a structural resume from a saved HEAD to current HEAD.
|
|
31
|
-
*
|
|
32
|
-
* Uses plumbing-safe git commands (diff-tree, rev-list, log) to
|
|
33
|
-
* find changed files and count commits between two points.
|
|
34
|
-
*/
|
|
35
|
-
export async function buildSessionResume(
|
|
36
|
-
options: SessionResumeOptions,
|
|
37
|
-
): Promise<SessionResumeResult> {
|
|
38
|
-
const { cwd, savedHeadSha, git } = options;
|
|
39
|
-
|
|
40
|
-
const headResult = await git.run({ cwd, args: ["rev-parse", "HEAD"] });
|
|
41
|
-
const currentHead = headResult.stdout.trim();
|
|
42
|
-
if (headResult.status !== 0 || currentHead.length === 0) {
|
|
43
|
-
return {
|
|
44
|
-
resumable: false, currentHead: "", savedHead: savedHeadSha,
|
|
45
|
-
changedFiles: [], commitsBehind: 0,
|
|
46
|
-
error: "Cannot resolve current HEAD",
|
|
47
|
-
};
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// Check if saved HEAD exists in the repo
|
|
51
|
-
const verifyResult = await git.run({ cwd, args: ["cat-file", "-t", savedHeadSha] });
|
|
52
|
-
if (verifyResult.stdout.trim() !== "commit") {
|
|
53
|
-
return {
|
|
54
|
-
resumable: false, currentHead, savedHead: savedHeadSha,
|
|
55
|
-
changedFiles: [], commitsBehind: 0,
|
|
56
|
-
error: "Saved HEAD SHA not found in repository",
|
|
57
|
-
};
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
if (currentHead === savedHeadSha) {
|
|
61
|
-
return {
|
|
62
|
-
resumable: true, currentHead, savedHead: savedHeadSha,
|
|
63
|
-
changedFiles: [], commitsBehind: 0,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Use diff-tree (plumbing-safe) with -r for recursive
|
|
68
|
-
const diffResult = await git.run({
|
|
69
|
-
cwd,
|
|
70
|
-
args: ["diff-tree", "-r", "--name-status", "--no-commit-id", savedHeadSha, currentHead],
|
|
71
|
-
});
|
|
72
|
-
const changedFiles: ResumeChangedFile[] = [];
|
|
73
|
-
const diffOutput = diffResult.stdout.trim();
|
|
74
|
-
|
|
75
|
-
if (diffOutput.length > 0) {
|
|
76
|
-
for (const line of diffOutput.split("\n")) {
|
|
77
|
-
const parts = line.split("\t");
|
|
78
|
-
if (parts.length < 2) continue;
|
|
79
|
-
const statusChar = (parts[0] ?? "").charAt(0);
|
|
80
|
-
const filePath = parts[parts.length - 1] ?? "";
|
|
81
|
-
|
|
82
|
-
let status: ResumeChangedFile["status"];
|
|
83
|
-
switch (statusChar) {
|
|
84
|
-
case "A": status = "added"; break;
|
|
85
|
-
case "D": status = "deleted"; break;
|
|
86
|
-
case "R": status = "renamed"; break;
|
|
87
|
-
default: status = "modified"; break;
|
|
88
|
-
}
|
|
89
|
-
changedFiles.push({ path: filePath, status });
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Count commits
|
|
94
|
-
const countResult = await git.run({
|
|
95
|
-
cwd,
|
|
96
|
-
args: ["rev-list", "--count", `${savedHeadSha}..${currentHead}`],
|
|
97
|
-
});
|
|
98
|
-
const commitsBehind = parseInt(countResult.stdout.trim(), 10) || 0;
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
resumable: true, currentHead, savedHead: savedHeadSha,
|
|
102
|
-
changedFiles, commitsBehind,
|
|
103
|
-
};
|
|
104
|
-
}
|
|
@@ -1,143 +0,0 @@
|
|
|
1
|
-
// ---------------------------------------------------------------------------
|
|
2
|
-
// Deterministic Scenario Replay — re-drive sessions for CI regression
|
|
3
|
-
// ---------------------------------------------------------------------------
|
|
4
|
-
|
|
5
|
-
import type { FileSystem } from "../ports/filesystem.js";
|
|
6
|
-
|
|
7
|
-
/** A recorded tool call with its expected result. */
|
|
8
|
-
export interface RecordedToolCall {
|
|
9
|
-
readonly tool: string;
|
|
10
|
-
readonly args: Readonly<Record<string, unknown>>;
|
|
11
|
-
readonly expectedResult: Readonly<Record<string, unknown>>;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/** A replay scenario: filesystem snapshot + recorded calls. */
|
|
15
|
-
interface _ReplayScenario {
|
|
16
|
-
readonly files: Readonly<Record<string, string>>;
|
|
17
|
-
readonly calls: readonly RecordedToolCall[];
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** A single mismatch between expected and actual. */
|
|
21
|
-
export interface ReplayFailure {
|
|
22
|
-
readonly index: number;
|
|
23
|
-
readonly tool: string;
|
|
24
|
-
readonly expected: Readonly<Record<string, unknown>>;
|
|
25
|
-
readonly actual: Readonly<Record<string, unknown>>;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Result of replaying a scenario. */
|
|
29
|
-
export interface ReplayResult {
|
|
30
|
-
readonly passed: boolean;
|
|
31
|
-
readonly total: number;
|
|
32
|
-
readonly matched: number;
|
|
33
|
-
readonly mismatched: number;
|
|
34
|
-
readonly failures: readonly ReplayFailure[];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/** Handler that executes a tool call and returns the result. */
|
|
38
|
-
export type ToolCallHandler = (
|
|
39
|
-
tool: string,
|
|
40
|
-
args: Readonly<Record<string, unknown>>,
|
|
41
|
-
) => Promise<Readonly<Record<string, unknown>>>;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Create a mock FileSystem from a snapshot map.
|
|
45
|
-
*
|
|
46
|
-
* Keys are file paths, values are file contents. Provides readFile,
|
|
47
|
-
* readdir, stat, and other FileSystem methods backed by the map.
|
|
48
|
-
*/
|
|
49
|
-
export function createSnapshotFs(
|
|
50
|
-
files: Readonly<Record<string, string>>,
|
|
51
|
-
): FileSystem {
|
|
52
|
-
const fileMap = new Map(Object.entries(files));
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
readFile(path: string, _encoding?: string): Promise<string> {
|
|
56
|
-
const content = fileMap.get(path);
|
|
57
|
-
if (content === undefined) {
|
|
58
|
-
return Promise.reject(new Error(`File not found in snapshot: ${path}`));
|
|
59
|
-
}
|
|
60
|
-
return Promise.resolve(content);
|
|
61
|
-
},
|
|
62
|
-
|
|
63
|
-
readFileSync(path: string): string {
|
|
64
|
-
const content = fileMap.get(path);
|
|
65
|
-
if (content === undefined) {
|
|
66
|
-
throw new Error(`File not found in snapshot: ${path}`);
|
|
67
|
-
}
|
|
68
|
-
return content;
|
|
69
|
-
},
|
|
70
|
-
|
|
71
|
-
readdir(dirPath: string): Promise<string[]> {
|
|
72
|
-
const prefix = dirPath.endsWith("/") ? dirPath : `${dirPath}/`;
|
|
73
|
-
const entries = new Set<string>();
|
|
74
|
-
for (const key of fileMap.keys()) {
|
|
75
|
-
if (key.startsWith(prefix)) {
|
|
76
|
-
const rest = key.slice(prefix.length);
|
|
77
|
-
const firstSegment = rest.split("/")[0];
|
|
78
|
-
if (firstSegment !== undefined && firstSegment.length > 0) {
|
|
79
|
-
entries.add(firstSegment);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return Promise.resolve([...entries].sort());
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
writeFile(): Promise<void> {
|
|
87
|
-
return Promise.resolve();
|
|
88
|
-
},
|
|
89
|
-
|
|
90
|
-
appendFile(): Promise<void> {
|
|
91
|
-
return Promise.resolve();
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
mkdir(): Promise<void> {
|
|
95
|
-
return Promise.resolve();
|
|
96
|
-
},
|
|
97
|
-
|
|
98
|
-
stat(path: string): Promise<{ size: number }> {
|
|
99
|
-
const content = fileMap.get(path);
|
|
100
|
-
return Promise.resolve({ size: content?.length ?? 0 });
|
|
101
|
-
},
|
|
102
|
-
} as unknown as FileSystem;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Replay a sequence of recorded tool calls against a handler.
|
|
107
|
-
*
|
|
108
|
-
* Compares each actual result to the expected result from the
|
|
109
|
-
* recording. Returns pass/fail with details on mismatches.
|
|
110
|
-
*/
|
|
111
|
-
export async function replayToolCalls(
|
|
112
|
-
calls: readonly RecordedToolCall[],
|
|
113
|
-
handler: ToolCallHandler,
|
|
114
|
-
): Promise<ReplayResult> {
|
|
115
|
-
const failures: ReplayFailure[] = [];
|
|
116
|
-
let matched = 0;
|
|
117
|
-
|
|
118
|
-
for (let i = 0; i < calls.length; i++) {
|
|
119
|
-
const call = calls[i] ?? { tool: "", args: {}, expectedResult: {} };
|
|
120
|
-
const actual = await handler(call.tool, call.args);
|
|
121
|
-
const expectedJson = JSON.stringify(call.expectedResult);
|
|
122
|
-
const actualJson = JSON.stringify(actual);
|
|
123
|
-
|
|
124
|
-
if (expectedJson === actualJson) {
|
|
125
|
-
matched++;
|
|
126
|
-
} else {
|
|
127
|
-
failures.push({
|
|
128
|
-
index: i,
|
|
129
|
-
tool: call.tool,
|
|
130
|
-
expected: call.expectedResult,
|
|
131
|
-
actual,
|
|
132
|
-
});
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
passed: failures.length === 0,
|
|
138
|
-
total: calls.length,
|
|
139
|
-
matched,
|
|
140
|
-
mismatched: failures.length,
|
|
141
|
-
failures,
|
|
142
|
-
};
|
|
143
|
-
}
|