copilot-tap-extension 2.0.7 → 2.0.9
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/README.md +4 -1
- package/SOUL.md +51 -0
- package/bin/install.mjs +7 -1
- package/dist/copilot-instructions.md +15 -0
- package/dist/extension.mjs +823 -29
- package/dist/skills/tap-goal/SKILL.md +13 -2
- package/dist/skills/tap-loop/SKILL.md +6 -0
- package/dist/skills/tap-monitor/SKILL.md +19 -3
- package/dist/skills/tap-orchestrate/SKILL.md +81 -0
- package/dist/version.json +1 -1
- package/docs/adr/0001-persistent-config-default-ownership.md +33 -0
- package/docs/adr/0002-local-provider-gateway-runtime-security.md +36 -0
- package/docs/adr/0003-emitter-delivery-lifecycle.md +68 -0
- package/docs/adr/0004-persistent-config-canonical-streams.md +86 -0
- package/docs/adr/0005-provider-sdk-push-and-dynamic-tools.md +48 -0
- package/docs/adr/0006-command-emitter-cwd-workspace-boundary.md +46 -0
- package/docs/adr/0007-runtime-session-workspace-context.md +62 -0
- package/docs/evals.md +41 -0
- package/docs/evolution-of-tap-icon.html +989 -0
- package/docs/providers.md +242 -0
- package/docs/recipes/adaptive-agent.md +303 -0
- package/docs/recipes/agent-brainstorm/100-extension-ideas.md +288 -0
- package/docs/recipes/agent-brainstorm/deep-ideas.md +216 -0
- package/docs/recipes/ambient-guardian.md +314 -0
- package/docs/recipes/browser-bridge.md +162 -0
- package/docs/recipes/codex-goals-for-tap-goal.md +136 -0
- package/docs/recipes/copilot-sdk-canvas.md +147 -0
- package/docs/recipes/deferred-cognition.md +310 -0
- package/docs/recipes/provider-integration-patterns.md +93 -0
- package/docs/recipes/provider-interface-advanced.md +1364 -0
- package/docs/recipes/provider-interface-core-profile.md +568 -0
- package/docs/recipes/tap-control-plane-roadmap.md +60 -0
- package/docs/recipes/universal-tool-gateway.md +202 -0
- package/docs/reference.md +229 -0
- package/docs/use-cases.md +348 -0
- package/package.json +4 -1
- package/providers/detour/README.md +84 -0
- package/providers/detour/bridge.js +219 -0
- package/providers/detour/index.mjs +322 -0
- package/providers/detour/package-lock.json +577 -0
- package/providers/detour/package.json +19 -0
- package/providers/detour/scripts/build.mjs +31 -0
- package/providers/detour/src/bridge.js +256 -0
- package/providers/detour/src/contracts.js +40 -0
- package/providers/detour/src/inspector.js +260 -0
- package/providers/detour/src/inspector.test.mjs +53 -0
- package/providers/detour/src/panel.js +465 -0
- package/providers/detour/src/provider-core.js +233 -0
- package/providers/detour/src/provider-core.test.mjs +185 -0
- package/providers/detour/src/react-context-core.js +143 -0
- package/providers/detour/src/react-context.js +44 -0
- package/providers/detour/src/react-context.test.mjs +41 -0
- package/providers/templates/README.md +23 -0
- package/providers/templates/ci-review-provider.mjs +46 -0
- package/providers/templates/detour-workflow-provider.mjs +41 -0
- package/providers/templates/jira-github-provider.mjs +42 -0
- package/providers/templates/provider-utils.mjs +45 -0
- package/providers/templates/sast-triage-provider.mjs +51 -0
package/dist/extension.mjs
CHANGED
|
@@ -4654,12 +4654,31 @@ function renderCanvasOpenResult(result) {
|
|
|
4654
4654
|
result?.availability ? `availability=${result.availability}` : null
|
|
4655
4655
|
].filter(Boolean).join("\n");
|
|
4656
4656
|
}
|
|
4657
|
+
function summarizeRuntimeState(state) {
|
|
4658
|
+
const mode = state?.mode?.ok ? state.mode.value : `unavailable (${state?.mode?.error ?? "unknown"})`;
|
|
4659
|
+
const model = state?.model?.ok ? state.model.value : null;
|
|
4660
|
+
const taskCount = state?.tasks?.ok ? state.tasks.value?.tasks?.length ?? 0 : null;
|
|
4661
|
+
const scheduleCount = state?.schedules?.ok ? state.schedules.value?.entries?.length ?? 0 : null;
|
|
4662
|
+
const permissionCount = state?.permissions?.ok ? state.permissions.value?.items?.length ?? 0 : null;
|
|
4663
|
+
const canvasCount = state?.openCanvases?.ok ? state.openCanvases.value?.openCanvases?.length ?? 0 : null;
|
|
4664
|
+
return [
|
|
4665
|
+
`sessionId=${state?.sessionId ?? "(none)"}`,
|
|
4666
|
+
`mode=${typeof mode === "string" ? mode : JSON.stringify(mode)}`,
|
|
4667
|
+
model ? `model=${model.modelId ?? "unknown"} reasoning=${model.reasoningEffort ?? "default"} context=${model.contextTier ?? "default"}` : null,
|
|
4668
|
+
taskCount !== null ? `tasks=${taskCount}` : null,
|
|
4669
|
+
scheduleCount !== null ? `schedules=${scheduleCount}` : null,
|
|
4670
|
+
permissionCount !== null ? `pendingPermissions=${permissionCount}` : null,
|
|
4671
|
+
canvasCount !== null ? `openCanvases=${canvasCount}` : null,
|
|
4672
|
+
`elicitation=${state?.capabilities?.ui?.elicitation === true ? "available" : "unavailable"}`,
|
|
4673
|
+
`canvases=${state?.capabilities?.ui?.canvases === true ? "available" : "host-gated"}`
|
|
4674
|
+
].filter(Boolean).join("\n");
|
|
4675
|
+
}
|
|
4657
4676
|
function createDiagnosticsTools(deps) {
|
|
4658
4677
|
const diagnostics2 = deps.diagnostics ?? deps.runtime;
|
|
4659
4678
|
if (!diagnostics2 || typeof diagnostics2.openCanvas !== "function") {
|
|
4660
4679
|
return [];
|
|
4661
4680
|
}
|
|
4662
|
-
|
|
4681
|
+
const tools = [
|
|
4663
4682
|
{
|
|
4664
4683
|
name: "tap_open_diagnostics_canvas",
|
|
4665
4684
|
description: "Opens or focuses the Tap diagnostics canvas, a live flight recorder for streams, emitters, providers, logs, injection queues, and session events.",
|
|
@@ -4684,6 +4703,163 @@ function createDiagnosticsTools(deps) {
|
|
|
4684
4703
|
})
|
|
4685
4704
|
}
|
|
4686
4705
|
];
|
|
4706
|
+
if (typeof diagnostics2.getSessionRuntimeState === "function") {
|
|
4707
|
+
tools.push({
|
|
4708
|
+
name: "tap_get_session_state",
|
|
4709
|
+
description: "Reads current Copilot session runtime state for mode-aware tap workflows: mode, model, tasks, schedules, open canvases, and UI capabilities. This is read-only.",
|
|
4710
|
+
parameters: {
|
|
4711
|
+
type: "object",
|
|
4712
|
+
properties: {}
|
|
4713
|
+
},
|
|
4714
|
+
handler: wrapToolHandler("tap_get_session_state", {}, async () => {
|
|
4715
|
+
const state = await diagnostics2.getSessionRuntimeState();
|
|
4716
|
+
return summarizeRuntimeState(state);
|
|
4717
|
+
})
|
|
4718
|
+
});
|
|
4719
|
+
if (typeof diagnostics2.setSessionMode === "function") {
|
|
4720
|
+
tools.push({
|
|
4721
|
+
name: "tap_set_session_mode",
|
|
4722
|
+
description: "Guarded Copilot mode switch for tap workflows. Requires explicit confirmation text and is intended for interactive/plan/autopilot transitions only.",
|
|
4723
|
+
parameters: {
|
|
4724
|
+
type: "object",
|
|
4725
|
+
properties: {
|
|
4726
|
+
mode: {
|
|
4727
|
+
type: "string",
|
|
4728
|
+
enum: ["interactive", "plan", "autopilot"],
|
|
4729
|
+
description: "Target Copilot session mode."
|
|
4730
|
+
},
|
|
4731
|
+
reason: {
|
|
4732
|
+
type: "string",
|
|
4733
|
+
description: "Why this mode switch is needed."
|
|
4734
|
+
},
|
|
4735
|
+
confirm: {
|
|
4736
|
+
type: "string",
|
|
4737
|
+
description: "Must be exactly 'set-session-mode' to confirm this user-visible mode change."
|
|
4738
|
+
}
|
|
4739
|
+
},
|
|
4740
|
+
required: ["mode", "reason", "confirm"]
|
|
4741
|
+
},
|
|
4742
|
+
handler: wrapToolHandler("tap_set_session_mode", (args) => ({ mode: args.mode }), async (args) => {
|
|
4743
|
+
if (args.confirm !== "set-session-mode") {
|
|
4744
|
+
throw new Error("Refusing to change session mode without confirm='set-session-mode'.");
|
|
4745
|
+
}
|
|
4746
|
+
const nextMode = await diagnostics2.setSessionMode(args.mode);
|
|
4747
|
+
return `Session mode set to ${nextMode}. reason=${args.reason}`;
|
|
4748
|
+
})
|
|
4749
|
+
});
|
|
4750
|
+
}
|
|
4751
|
+
}
|
|
4752
|
+
if (typeof diagnostics2.queryRecords === "function") {
|
|
4753
|
+
tools.push({
|
|
4754
|
+
name: "tap_query_records",
|
|
4755
|
+
description: "Reads structured tap records persisted in the session workspace, such as traces and stream-posts.",
|
|
4756
|
+
parameters: {
|
|
4757
|
+
type: "object",
|
|
4758
|
+
properties: {
|
|
4759
|
+
collection: {
|
|
4760
|
+
type: "string",
|
|
4761
|
+
description: "Record collection name, for example 'traces' or 'stream-posts'."
|
|
4762
|
+
},
|
|
4763
|
+
limit: {
|
|
4764
|
+
type: "integer",
|
|
4765
|
+
minimum: 1,
|
|
4766
|
+
maximum: 500,
|
|
4767
|
+
description: "Maximum recent records to return."
|
|
4768
|
+
}
|
|
4769
|
+
},
|
|
4770
|
+
required: ["collection"]
|
|
4771
|
+
},
|
|
4772
|
+
handler: wrapToolHandler("tap_query_records", (args) => ({ collection: args.collection }), async (args) => {
|
|
4773
|
+
const result = diagnostics2.queryRecords(args.collection, { limit: args.limit });
|
|
4774
|
+
return JSON.stringify(result, null, 2);
|
|
4775
|
+
})
|
|
4776
|
+
});
|
|
4777
|
+
}
|
|
4778
|
+
return tools;
|
|
4779
|
+
}
|
|
4780
|
+
|
|
4781
|
+
// src/tools/goal-verification.mjs
|
|
4782
|
+
function renderVerification(prefix, result) {
|
|
4783
|
+
const lines = [
|
|
4784
|
+
`${prefix}: ${result.passed ? "passed" : "failed"}`,
|
|
4785
|
+
...result.results.map((item) => {
|
|
4786
|
+
const label = item.description ?? item.claim ?? item.path ?? item.channel ?? item.label ?? `check ${item.index}`;
|
|
4787
|
+
return `- ${item.passed ? "PASS" : "FAIL"} ${label}${item.error ? ` \u2014 ${item.error}` : ""}`;
|
|
4788
|
+
})
|
|
4789
|
+
];
|
|
4790
|
+
return lines.join("\n");
|
|
4791
|
+
}
|
|
4792
|
+
var CHECK_SCHEMA = {
|
|
4793
|
+
type: "object",
|
|
4794
|
+
properties: {
|
|
4795
|
+
type: {
|
|
4796
|
+
type: "string",
|
|
4797
|
+
description: "Check type: 'file', 'stream', or 'command_evidence'."
|
|
4798
|
+
},
|
|
4799
|
+
description: { type: "string" },
|
|
4800
|
+
path: { type: "string", description: "Workspace-relative file path for file checks." },
|
|
4801
|
+
nonEmpty: { type: "boolean", description: "For file checks, require a non-empty file." },
|
|
4802
|
+
channel: { type: "string", description: "EventStream name for stream checks." },
|
|
4803
|
+
limit: { type: "integer", description: "Recent stream entries to inspect." },
|
|
4804
|
+
minEntries: { type: "integer", description: "Minimum retained entries required." },
|
|
4805
|
+
contains: { type: "string", description: "Literal text that must be present." },
|
|
4806
|
+
pattern: { type: "string", description: "Regex pattern that must match." },
|
|
4807
|
+
label: { type: "string", description: "Human label for command evidence." },
|
|
4808
|
+
exitCode: { type: "integer", description: "Exit code from an already-run command." },
|
|
4809
|
+
success: { type: "boolean", description: "Whether already-run command evidence succeeded." }
|
|
4810
|
+
}
|
|
4811
|
+
};
|
|
4812
|
+
function createGoalVerificationTools(deps) {
|
|
4813
|
+
const verification = deps.verification ?? deps.runtime;
|
|
4814
|
+
if (!verification || typeof verification.verifyGoalOutput !== "function") {
|
|
4815
|
+
return [];
|
|
4816
|
+
}
|
|
4817
|
+
return [
|
|
4818
|
+
{
|
|
4819
|
+
name: "tap_verify_goal_output",
|
|
4820
|
+
description: "Verifies goal completion evidence without executing commands. Checks workspace files, EventStream history, or caller-supplied command evidence.",
|
|
4821
|
+
parameters: {
|
|
4822
|
+
type: "object",
|
|
4823
|
+
properties: {
|
|
4824
|
+
checks: {
|
|
4825
|
+
type: "array",
|
|
4826
|
+
items: CHECK_SCHEMA,
|
|
4827
|
+
description: "Evidence checks to perform. Command checks must use already-run command evidence; this tool does not execute shell commands."
|
|
4828
|
+
}
|
|
4829
|
+
},
|
|
4830
|
+
required: ["checks"]
|
|
4831
|
+
},
|
|
4832
|
+
handler: wrapToolHandler("tap_verify_goal_output", {}, async (args) => {
|
|
4833
|
+
const result = verification.verifyGoalOutput(args ?? {});
|
|
4834
|
+
return renderVerification("Goal output verification", result);
|
|
4835
|
+
})
|
|
4836
|
+
},
|
|
4837
|
+
{
|
|
4838
|
+
name: "tap_audit_claims",
|
|
4839
|
+
description: "Audits machine-readable goal claims against file, stream, or command-evidence surfaces before marking a goal complete.",
|
|
4840
|
+
parameters: {
|
|
4841
|
+
type: "object",
|
|
4842
|
+
properties: {
|
|
4843
|
+
claims: {
|
|
4844
|
+
type: "array",
|
|
4845
|
+
items: {
|
|
4846
|
+
type: "object",
|
|
4847
|
+
properties: {
|
|
4848
|
+
claim: { type: "string" },
|
|
4849
|
+
evidence: CHECK_SCHEMA
|
|
4850
|
+
},
|
|
4851
|
+
required: ["claim", "evidence"]
|
|
4852
|
+
}
|
|
4853
|
+
}
|
|
4854
|
+
},
|
|
4855
|
+
required: ["claims"]
|
|
4856
|
+
},
|
|
4857
|
+
handler: wrapToolHandler("tap_audit_claims", {}, async (args) => {
|
|
4858
|
+
const result = verification.auditClaims(args ?? {});
|
|
4859
|
+
return renderVerification("Claim audit", result);
|
|
4860
|
+
})
|
|
4861
|
+
}
|
|
4862
|
+
];
|
|
4687
4863
|
}
|
|
4688
4864
|
|
|
4689
4865
|
// src/tools/index.mjs
|
|
@@ -4692,10 +4868,12 @@ function createTools(deps) {
|
|
|
4692
4868
|
const streams = deps?.streams ?? source.streams ?? deps?.runtime;
|
|
4693
4869
|
const emitters = deps?.emitters ?? source.emitters ?? deps?.runtime;
|
|
4694
4870
|
const diagnostics2 = deps?.diagnostics ?? source.diagnostics ?? deps?.runtime;
|
|
4871
|
+
const verification = deps?.verification ?? source.verification ?? deps?.runtime;
|
|
4695
4872
|
return [
|
|
4696
4873
|
...createStreamTools({ streams }),
|
|
4697
4874
|
...createEmitterTools({ emitters }),
|
|
4698
|
-
...createDiagnosticsTools({ diagnostics: diagnostics2 })
|
|
4875
|
+
...createDiagnosticsTools({ diagnostics: diagnostics2 }),
|
|
4876
|
+
...createGoalVerificationTools({ verification })
|
|
4699
4877
|
];
|
|
4700
4878
|
}
|
|
4701
4879
|
|
|
@@ -6801,6 +6979,10 @@ function createProviderGateway(options = {}, adapters = {}) {
|
|
|
6801
6979
|
};
|
|
6802
6980
|
}
|
|
6803
6981
|
|
|
6982
|
+
// src/services/tap-runtime-service.mjs
|
|
6983
|
+
import fs4 from "node:fs";
|
|
6984
|
+
import path9 from "node:path";
|
|
6985
|
+
|
|
6804
6986
|
// src/session/listeners.mjs
|
|
6805
6987
|
var SESSION_ACTIVITY_EVENTS = [
|
|
6806
6988
|
"session.start",
|
|
@@ -8632,7 +8814,7 @@ function shouldPersistLoadedConfig(parsedConfig, normalizedConfig) {
|
|
|
8632
8814
|
return !isDeepStrictEqual(parsedConfig, serializeConfigForComparison(normalizedConfig));
|
|
8633
8815
|
}
|
|
8634
8816
|
function createConfigStore(options = {}) {
|
|
8635
|
-
const
|
|
8817
|
+
const fs5 = options.fs ?? { existsSync: existsSync2, readFileSync: readFileSync2, writeFileSync: writeFileSync2 };
|
|
8636
8818
|
const state = {
|
|
8637
8819
|
cwd: normalizeBaseCwd(options.cwd),
|
|
8638
8820
|
filePath: null,
|
|
@@ -8660,7 +8842,7 @@ function createConfigStore(options = {}) {
|
|
|
8660
8842
|
const exists = withConfigLoadPhase(
|
|
8661
8843
|
"checking config path",
|
|
8662
8844
|
"Unable to check whether the tap config file exists.",
|
|
8663
|
-
() =>
|
|
8845
|
+
() => fs5.existsSync(filePath),
|
|
8664
8846
|
{ filePath }
|
|
8665
8847
|
);
|
|
8666
8848
|
if (!exists) {
|
|
@@ -8669,7 +8851,7 @@ function createConfigStore(options = {}) {
|
|
|
8669
8851
|
const rawConfig = withConfigLoadPhase(
|
|
8670
8852
|
"reading config file",
|
|
8671
8853
|
"Unable to read the tap config file.",
|
|
8672
|
-
() =>
|
|
8854
|
+
() => fs5.readFileSync(filePath, "utf8"),
|
|
8673
8855
|
{ filePath }
|
|
8674
8856
|
);
|
|
8675
8857
|
const parsedConfig = withConfigLoadPhase(
|
|
@@ -8727,7 +8909,7 @@ function createConfigStore(options = {}) {
|
|
|
8727
8909
|
state.filePath = defaultConfigPath(state.cwd);
|
|
8728
8910
|
}
|
|
8729
8911
|
const payload = serializeConfig(state.config, LATEST_CONFIG_VERSION);
|
|
8730
|
-
|
|
8912
|
+
fs5.writeFileSync(state.filePath, `${JSON.stringify(payload, null, 2)}
|
|
8731
8913
|
`, "utf8");
|
|
8732
8914
|
}
|
|
8733
8915
|
function findStreamIndex(name) {
|
|
@@ -9231,9 +9413,64 @@ function createDefaultProcessAdapter() {
|
|
|
9231
9413
|
readLines
|
|
9232
9414
|
};
|
|
9233
9415
|
}
|
|
9416
|
+
function recordScheduledTrace(emitter, context, { startedAt, endedAt = nowIso(), runIndex, result = null, error = null, consumedRun = true } = {}) {
|
|
9417
|
+
try {
|
|
9418
|
+
const start = startedAt ?? endedAt;
|
|
9419
|
+
const status = result?.deferred ? "deferred" : result?.ok ? "success" : "failure";
|
|
9420
|
+
context.diagnostics?.trace?.({
|
|
9421
|
+
traceId: `${stableTraceComponent(emitter.name)}-${Number(runIndex ?? emitter.runCount) || 0}-${Date.parse(start) || Date.now()}`,
|
|
9422
|
+
emitterId: emitter.name,
|
|
9423
|
+
emitterName: emitter.name,
|
|
9424
|
+
runIndex: Number(runIndex ?? emitter.runCount) || null,
|
|
9425
|
+
emitterType: emitter.emitterType,
|
|
9426
|
+
runSchedule: emitter.runSchedule,
|
|
9427
|
+
startedAt: start,
|
|
9428
|
+
endedAt,
|
|
9429
|
+
status,
|
|
9430
|
+
ok: result?.ok === true,
|
|
9431
|
+
consumedRun,
|
|
9432
|
+
lineCount: emitter.lineCount,
|
|
9433
|
+
droppedLineCount: emitter.droppedLineCount,
|
|
9434
|
+
error: error ?? result?.error ?? null,
|
|
9435
|
+
spans: [
|
|
9436
|
+
{
|
|
9437
|
+
spanId: "emitter-run",
|
|
9438
|
+
kind: "emitter.run",
|
|
9439
|
+
name: emitter.name,
|
|
9440
|
+
startedAt: start,
|
|
9441
|
+
endedAt,
|
|
9442
|
+
status
|
|
9443
|
+
},
|
|
9444
|
+
{
|
|
9445
|
+
spanId: emitter.emitterType === EMITTER_TYPE.PROMPT ? "prompt-dispatch" : "command-process",
|
|
9446
|
+
parentSpanId: "emitter-run",
|
|
9447
|
+
kind: emitter.emitterType === EMITTER_TYPE.PROMPT ? "prompt.dispatch" : "command.process",
|
|
9448
|
+
name: emitter.emitterType,
|
|
9449
|
+
startedAt: start,
|
|
9450
|
+
endedAt,
|
|
9451
|
+
status,
|
|
9452
|
+
metadata: {
|
|
9453
|
+
stream: emitter.stream,
|
|
9454
|
+
cwd: emitter.cwd ?? null
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
],
|
|
9458
|
+
metadata: {
|
|
9459
|
+
every: emitter.every ?? null,
|
|
9460
|
+
everySchedule: emitter.everySchedule ?? null,
|
|
9461
|
+
maxRuns: emitter.maxRuns ?? null,
|
|
9462
|
+
stopRequested: emitter.stopRequested === true
|
|
9463
|
+
}
|
|
9464
|
+
});
|
|
9465
|
+
} catch {
|
|
9466
|
+
}
|
|
9467
|
+
}
|
|
9234
9468
|
var SESSION_ATTACH_RETRY_MS = 100;
|
|
9235
9469
|
var DEFAULT_STOP_WAIT_TIMEOUT_MS = 1e4;
|
|
9236
9470
|
var STOP_WAIT_POLL_MS = 50;
|
|
9471
|
+
function stableTraceComponent(value) {
|
|
9472
|
+
return String(value ?? "unknown").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
9473
|
+
}
|
|
9237
9474
|
function errorMessage(error) {
|
|
9238
9475
|
return error?.message ?? String(error ?? "unknown error");
|
|
9239
9476
|
}
|
|
@@ -9569,6 +9806,8 @@ async function runScheduledIteration(emitter, context) {
|
|
|
9569
9806
|
emitter.status = EMITTER_STATUS.RUNNING;
|
|
9570
9807
|
emitter.runCount += 1;
|
|
9571
9808
|
emitter.lastRunAt = nowIso();
|
|
9809
|
+
const traceStartedAt = emitter.lastRunAt;
|
|
9810
|
+
const traceRunIndex = emitter.runCount;
|
|
9572
9811
|
let result;
|
|
9573
9812
|
try {
|
|
9574
9813
|
result = emitter.emitterType === EMITTER_TYPE.PROMPT ? await runPromptIteration(emitter, context) : await runCommandLoopIteration(emitter, context);
|
|
@@ -9586,6 +9825,12 @@ async function runScheduledIteration(emitter, context) {
|
|
|
9586
9825
|
if (result?.consumeRun === false) {
|
|
9587
9826
|
emitter.runCount = previousRunCount;
|
|
9588
9827
|
emitter.lastRunAt = previousLastRunAt;
|
|
9828
|
+
recordScheduledTrace(emitter, context, {
|
|
9829
|
+
startedAt: traceStartedAt,
|
|
9830
|
+
runIndex: traceRunIndex,
|
|
9831
|
+
result,
|
|
9832
|
+
consumedRun: false
|
|
9833
|
+
});
|
|
9589
9834
|
if (emitter.stopRequested) {
|
|
9590
9835
|
applyLifecycleTransition(emitter, {
|
|
9591
9836
|
type: LIFECYCLE_EVENT.ITERATION_RESULT,
|
|
@@ -9604,7 +9849,20 @@ async function runScheduledIteration(emitter, context) {
|
|
|
9604
9849
|
result,
|
|
9605
9850
|
timestamp: nowIso()
|
|
9606
9851
|
}, context);
|
|
9852
|
+
recordScheduledTrace(emitter, context, {
|
|
9853
|
+
startedAt: traceStartedAt,
|
|
9854
|
+
runIndex: traceRunIndex,
|
|
9855
|
+
result,
|
|
9856
|
+
consumedRun: true
|
|
9857
|
+
});
|
|
9607
9858
|
} catch (error) {
|
|
9859
|
+
recordScheduledTrace(emitter, context, {
|
|
9860
|
+
startedAt: traceStartedAt,
|
|
9861
|
+
runIndex: traceRunIndex,
|
|
9862
|
+
result: { ok: false, error: errorMessage(error) },
|
|
9863
|
+
error: errorMessage(error),
|
|
9864
|
+
consumedRun: true
|
|
9865
|
+
});
|
|
9608
9866
|
recordScheduledTransitionFailure(emitter, error, context);
|
|
9609
9867
|
} finally {
|
|
9610
9868
|
emitter.inFlight = false;
|
|
@@ -9618,7 +9876,8 @@ function createLifecycle({
|
|
|
9618
9876
|
sessionPort,
|
|
9619
9877
|
timerAdapter = createDefaultTimerAdapter(),
|
|
9620
9878
|
processAdapter = createDefaultProcessAdapter(),
|
|
9621
|
-
loggerAdapter = createDefaultLoggerAdapter(sessionPort)
|
|
9879
|
+
loggerAdapter = createDefaultLoggerAdapter(sessionPort),
|
|
9880
|
+
diagnostics: diagnostics2 = null
|
|
9622
9881
|
}) {
|
|
9623
9882
|
function start(emitter) {
|
|
9624
9883
|
if (emitter.runSchedule === RUN_SCHEDULE.CONTINUOUS) {
|
|
@@ -9628,6 +9887,7 @@ function createLifecycle({
|
|
|
9628
9887
|
contextStartScheduled(emitter);
|
|
9629
9888
|
}
|
|
9630
9889
|
function contextStartScheduled(emitter) {
|
|
9890
|
+
const context = { lineRouter, timerAdapter, processAdapter, loggerAdapter, sessionPort, diagnostics: diagnostics2 };
|
|
9631
9891
|
const scheduleLabel2 = emitter.runSchedule === RUN_SCHEDULE.TIMED ? emitter.everySchedule ? `backoff [${emitter.everySchedule.join(", ")}]` : `every ${emitter.every}` : emitter.runSchedule === RUN_SCHEDULE.IDLE ? "when idle" : RUN_SCHEDULE.ONE_TIME;
|
|
9632
9892
|
lineRouter.appendSystemMessage(
|
|
9633
9893
|
emitter,
|
|
@@ -9640,17 +9900,17 @@ function createLifecycle({
|
|
|
9640
9900
|
);
|
|
9641
9901
|
if (emitter.runSchedule === RUN_SCHEDULE.IDLE) {
|
|
9642
9902
|
if (sessionPort.isIdle()) {
|
|
9643
|
-
scheduleIteration(emitter,
|
|
9903
|
+
scheduleIteration(emitter, context, IDLE_PROMPT_DELAY_MS);
|
|
9644
9904
|
}
|
|
9645
9905
|
return;
|
|
9646
9906
|
}
|
|
9647
|
-
scheduleIteration(emitter,
|
|
9907
|
+
scheduleIteration(emitter, context, 0);
|
|
9648
9908
|
}
|
|
9649
9909
|
async function stop(emitter) {
|
|
9650
9910
|
applyLifecycleTransition(
|
|
9651
9911
|
emitter,
|
|
9652
9912
|
{ type: LIFECYCLE_EVENT.STOP, timestamp: nowIso() },
|
|
9653
|
-
{ lineRouter, timerAdapter, processAdapter, loggerAdapter, sessionPort }
|
|
9913
|
+
{ lineRouter, timerAdapter, processAdapter, loggerAdapter, sessionPort, diagnostics: diagnostics2 }
|
|
9654
9914
|
);
|
|
9655
9915
|
if (isTerminalEmitterStatus(emitter.status)) {
|
|
9656
9916
|
return;
|
|
@@ -9826,14 +10086,14 @@ function restorePersistentStreamConfigBestEffort(configStore, snapshot) {
|
|
|
9826
10086
|
|
|
9827
10087
|
// src/emitter/supervisor.mjs
|
|
9828
10088
|
var ROLLBACK_STOP_WAIT_TIMEOUT_MS = 1e4;
|
|
9829
|
-
function createEmitterSupervisor({ streams, configStore, notifications, sessionPort, emitterWorkspace, persist, lifecycle: lifecycleOverride }) {
|
|
10089
|
+
function createEmitterSupervisor({ streams, configStore, notifications, sessionPort, emitterWorkspace, persist, lifecycle: lifecycleOverride, diagnostics: diagnostics2 = null }) {
|
|
9830
10090
|
const emitters = /* @__PURE__ */ new Map();
|
|
9831
10091
|
const lineRouter = createLineRouter({
|
|
9832
10092
|
streams,
|
|
9833
10093
|
notifications,
|
|
9834
10094
|
surface: (message, options) => sessionPort.log(message, options)
|
|
9835
10095
|
});
|
|
9836
|
-
const lifecycle = lifecycleOverride ?? createLifecycle({ lineRouter, sessionPort });
|
|
10096
|
+
const lifecycle = lifecycleOverride ?? createLifecycle({ lineRouter, sessionPort, diagnostics: diagnostics2 });
|
|
9837
10097
|
function hasExplicitPolicyValue(value) {
|
|
9838
10098
|
return value !== void 0 && value !== null;
|
|
9839
10099
|
}
|
|
@@ -10367,6 +10627,41 @@ function createSessionPort(initialSession = null) {
|
|
|
10367
10627
|
}
|
|
10368
10628
|
return canvasApi.open(params);
|
|
10369
10629
|
}
|
|
10630
|
+
async function getRuntimeState() {
|
|
10631
|
+
if (!session2) {
|
|
10632
|
+
throw new LifecycleError("Session is not attached; cannot inspect runtime state.");
|
|
10633
|
+
}
|
|
10634
|
+
const rpc = session2.rpc ?? {};
|
|
10635
|
+
const read = async (label, fn) => {
|
|
10636
|
+
try {
|
|
10637
|
+
return { ok: true, value: await fn() };
|
|
10638
|
+
} catch (error) {
|
|
10639
|
+
return { ok: false, error: error?.message ?? String(error ?? "unknown error") };
|
|
10640
|
+
}
|
|
10641
|
+
};
|
|
10642
|
+
return {
|
|
10643
|
+
sessionId: session2.sessionId ?? null,
|
|
10644
|
+
capabilities: session2.capabilities ?? null,
|
|
10645
|
+
mode: await read("mode", () => rpc.mode?.get?.()),
|
|
10646
|
+
model: await read("model", () => rpc.model?.getCurrent?.()),
|
|
10647
|
+
tasks: await read("tasks", () => rpc.tasks?.list?.()),
|
|
10648
|
+
schedules: await read("schedules", () => rpc.schedule?.list?.()),
|
|
10649
|
+
skills: await read("skills", () => rpc.skills?.list?.()),
|
|
10650
|
+
permissions: await read("permissions", () => rpc.permissions?.pendingRequests?.()),
|
|
10651
|
+
openCanvases: await read("openCanvases", () => rpc.canvas?.listOpen?.())
|
|
10652
|
+
};
|
|
10653
|
+
}
|
|
10654
|
+
async function setMode(mode) {
|
|
10655
|
+
if (!session2) {
|
|
10656
|
+
throw new LifecycleError("Session is not attached; cannot set mode.");
|
|
10657
|
+
}
|
|
10658
|
+
const modeApi = session2.rpc?.mode;
|
|
10659
|
+
if (!modeApi || typeof modeApi.set !== "function") {
|
|
10660
|
+
throw new LifecycleError("Session mode API is not available in this Copilot session.");
|
|
10661
|
+
}
|
|
10662
|
+
await modeApi.set({ mode });
|
|
10663
|
+
return modeApi.get();
|
|
10664
|
+
}
|
|
10370
10665
|
function registerTools(tools) {
|
|
10371
10666
|
if (!session2) return;
|
|
10372
10667
|
try {
|
|
@@ -10403,6 +10698,8 @@ function createSessionPort(initialSession = null) {
|
|
|
10403
10698
|
send,
|
|
10404
10699
|
sendAndWait,
|
|
10405
10700
|
openCanvas,
|
|
10701
|
+
getRuntimeState,
|
|
10702
|
+
setMode,
|
|
10406
10703
|
registerTools,
|
|
10407
10704
|
reloadExtension
|
|
10408
10705
|
};
|
|
@@ -10683,7 +10980,8 @@ function createRuntimeSubsystems(options = {}) {
|
|
|
10683
10980
|
notifications,
|
|
10684
10981
|
sessionPort,
|
|
10685
10982
|
emitterWorkspace,
|
|
10686
|
-
persist
|
|
10983
|
+
persist,
|
|
10984
|
+
diagnostics: options.diagnostics
|
|
10687
10985
|
});
|
|
10688
10986
|
return {
|
|
10689
10987
|
sessionContext,
|
|
@@ -10772,7 +11070,7 @@ function projectStream(stream) {
|
|
|
10772
11070
|
|
|
10773
11071
|
// src/services/stream-service.mjs
|
|
10774
11072
|
function createStreamService(deps) {
|
|
10775
|
-
const { streams, configStore, sessionPort, persist } = deps;
|
|
11073
|
+
const { streams, configStore, sessionPort, persist, recordStore = null } = deps;
|
|
10776
11074
|
function requireStreamChannel(channel, operation) {
|
|
10777
11075
|
return requireNormalizedName(channel, {
|
|
10778
11076
|
label: "Stream channel",
|
|
@@ -10816,6 +11114,17 @@ function createStreamService(deps) {
|
|
|
10816
11114
|
context: { channel: stream.name }
|
|
10817
11115
|
});
|
|
10818
11116
|
}
|
|
11117
|
+
try {
|
|
11118
|
+
recordStore?.appendRecord?.("stream-posts", {
|
|
11119
|
+
channel: stream.name,
|
|
11120
|
+
source,
|
|
11121
|
+
text,
|
|
11122
|
+
timestamp: appended.timestamp,
|
|
11123
|
+
monitorName: appended.monitorName ?? null,
|
|
11124
|
+
metadata: appended.metadata ?? null
|
|
11125
|
+
});
|
|
11126
|
+
} catch {
|
|
11127
|
+
}
|
|
10819
11128
|
void sessionPort.log(`Posted message to stream '${stream.name}'.`);
|
|
10820
11129
|
return { stream: projectStream(stream) };
|
|
10821
11130
|
}
|
|
@@ -10858,10 +11167,256 @@ function createStreamService(deps) {
|
|
|
10858
11167
|
};
|
|
10859
11168
|
}
|
|
10860
11169
|
|
|
11170
|
+
// src/services/goal-verification-service.mjs
|
|
11171
|
+
import fs2 from "node:fs";
|
|
11172
|
+
import path7 from "node:path";
|
|
11173
|
+
function normalizeText(value) {
|
|
11174
|
+
return String(value ?? "").trim();
|
|
11175
|
+
}
|
|
11176
|
+
function safeRegex(pattern) {
|
|
11177
|
+
try {
|
|
11178
|
+
return new RegExp(String(pattern));
|
|
11179
|
+
} catch {
|
|
11180
|
+
return null;
|
|
11181
|
+
}
|
|
11182
|
+
}
|
|
11183
|
+
function resolveWithinBase(baseCwd, requestedPath) {
|
|
11184
|
+
const raw = normalizeText(requestedPath);
|
|
11185
|
+
if (!raw) {
|
|
11186
|
+
return { ok: false, error: "path is required" };
|
|
11187
|
+
}
|
|
11188
|
+
const base = path7.resolve(baseCwd || process.cwd());
|
|
11189
|
+
const resolved = path7.isAbsolute(raw) ? path7.resolve(raw) : path7.resolve(base, raw);
|
|
11190
|
+
const relative = path7.relative(base, resolved);
|
|
11191
|
+
if (relative.startsWith("..") || path7.isAbsolute(relative)) {
|
|
11192
|
+
return { ok: false, error: `path '${raw}' is outside the session workspace` };
|
|
11193
|
+
}
|
|
11194
|
+
return { ok: true, path: resolved, displayPath: relative || "." };
|
|
11195
|
+
}
|
|
11196
|
+
function textMatches(text, check = {}) {
|
|
11197
|
+
const contains = normalizeText(check.contains);
|
|
11198
|
+
if (contains && !String(text).includes(contains)) {
|
|
11199
|
+
return { ok: false, error: `expected text to contain '${contains}'` };
|
|
11200
|
+
}
|
|
11201
|
+
const pattern = normalizeText(check.pattern);
|
|
11202
|
+
if (pattern) {
|
|
11203
|
+
const regex = safeRegex(pattern);
|
|
11204
|
+
if (!regex) {
|
|
11205
|
+
return { ok: false, error: `invalid regex '${pattern}'` };
|
|
11206
|
+
}
|
|
11207
|
+
if (!regex.test(String(text))) {
|
|
11208
|
+
return { ok: false, error: `expected text to match /${pattern}/` };
|
|
11209
|
+
}
|
|
11210
|
+
}
|
|
11211
|
+
return { ok: true };
|
|
11212
|
+
}
|
|
11213
|
+
function verifyFile(check, { baseCwd }) {
|
|
11214
|
+
const resolved = resolveWithinBase(baseCwd, check.path);
|
|
11215
|
+
if (!resolved.ok) {
|
|
11216
|
+
return { ...resolved, passed: false };
|
|
11217
|
+
}
|
|
11218
|
+
if (!fs2.existsSync(resolved.path)) {
|
|
11219
|
+
return { passed: false, path: resolved.displayPath, error: "file does not exist" };
|
|
11220
|
+
}
|
|
11221
|
+
const stat = fs2.statSync(resolved.path);
|
|
11222
|
+
if (!stat.isFile()) {
|
|
11223
|
+
return { passed: false, path: resolved.displayPath, error: "path is not a file" };
|
|
11224
|
+
}
|
|
11225
|
+
if (check.nonEmpty === true && stat.size === 0) {
|
|
11226
|
+
return { passed: false, path: resolved.displayPath, error: "file is empty" };
|
|
11227
|
+
}
|
|
11228
|
+
if (check.contains || check.pattern) {
|
|
11229
|
+
const content = fs2.readFileSync(resolved.path, "utf8");
|
|
11230
|
+
const match = textMatches(content, check);
|
|
11231
|
+
if (!match.ok) {
|
|
11232
|
+
return { passed: false, path: resolved.displayPath, error: match.error };
|
|
11233
|
+
}
|
|
11234
|
+
}
|
|
11235
|
+
return { passed: true, path: resolved.displayPath, size: stat.size };
|
|
11236
|
+
}
|
|
11237
|
+
function verifyStream(check, { getStreamHistory }) {
|
|
11238
|
+
const channel = normalizeText(check.channel);
|
|
11239
|
+
if (!channel) {
|
|
11240
|
+
return { passed: false, error: "channel is required" };
|
|
11241
|
+
}
|
|
11242
|
+
let stream;
|
|
11243
|
+
try {
|
|
11244
|
+
stream = getStreamHistory(channel, check.limit)?.stream;
|
|
11245
|
+
} catch (error) {
|
|
11246
|
+
return { passed: false, channel, error: error?.message ?? String(error) };
|
|
11247
|
+
}
|
|
11248
|
+
const entries = Array.isArray(stream?.entries) ? stream.entries : [];
|
|
11249
|
+
if (check.minEntries !== void 0 && entries.length < Number(check.minEntries)) {
|
|
11250
|
+
return { passed: false, channel, entries: entries.length, error: `expected at least ${check.minEntries} entries` };
|
|
11251
|
+
}
|
|
11252
|
+
const text = entries.map((entry) => entry.text ?? "").join("\n");
|
|
11253
|
+
const match = textMatches(text, check);
|
|
11254
|
+
if (!match.ok) {
|
|
11255
|
+
return { passed: false, channel, entries: entries.length, error: match.error };
|
|
11256
|
+
}
|
|
11257
|
+
return { passed: true, channel, entries: entries.length };
|
|
11258
|
+
}
|
|
11259
|
+
function verifyCommandEvidence(check) {
|
|
11260
|
+
const label = normalizeText(check.label ?? check.command ?? "command evidence");
|
|
11261
|
+
if (check.exitCode === void 0 && check.success !== true) {
|
|
11262
|
+
return { passed: false, label, error: "provide exitCode or success=true from an already-run command" };
|
|
11263
|
+
}
|
|
11264
|
+
const passed = check.success === true || Number(check.exitCode) === 0;
|
|
11265
|
+
return {
|
|
11266
|
+
passed,
|
|
11267
|
+
label,
|
|
11268
|
+
exitCode: check.exitCode ?? null,
|
|
11269
|
+
error: passed ? null : `command evidence did not indicate success`
|
|
11270
|
+
};
|
|
11271
|
+
}
|
|
11272
|
+
function verifyCheck(check = {}, context) {
|
|
11273
|
+
const type = normalizeText(check.type || check.kind);
|
|
11274
|
+
if (type === "file" || type === "file_exists") {
|
|
11275
|
+
return verifyFile(check, context);
|
|
11276
|
+
}
|
|
11277
|
+
if (type === "stream" || type === "stream_contains" || type === "channel") {
|
|
11278
|
+
return verifyStream(check, context);
|
|
11279
|
+
}
|
|
11280
|
+
if (type === "command" || type === "command_evidence") {
|
|
11281
|
+
return verifyCommandEvidence(check);
|
|
11282
|
+
}
|
|
11283
|
+
return { passed: false, error: `unsupported check type '${type || "<missing>"}'` };
|
|
11284
|
+
}
|
|
11285
|
+
function createGoalVerificationService({ getBaseCwd, getStreamHistory } = {}) {
|
|
11286
|
+
function context() {
|
|
11287
|
+
return {
|
|
11288
|
+
baseCwd: typeof getBaseCwd === "function" ? getBaseCwd() : process.cwd(),
|
|
11289
|
+
getStreamHistory
|
|
11290
|
+
};
|
|
11291
|
+
}
|
|
11292
|
+
function verifyGoalOutput(input = {}) {
|
|
11293
|
+
const checks = Array.isArray(input.checks) ? input.checks : [];
|
|
11294
|
+
const results = checks.map((check, index) => ({
|
|
11295
|
+
index,
|
|
11296
|
+
description: check.description ?? check.claim ?? null,
|
|
11297
|
+
type: check.type ?? check.kind ?? null,
|
|
11298
|
+
...verifyCheck(check, context())
|
|
11299
|
+
}));
|
|
11300
|
+
return {
|
|
11301
|
+
passed: results.length > 0 && results.every((result) => result.passed === true),
|
|
11302
|
+
results
|
|
11303
|
+
};
|
|
11304
|
+
}
|
|
11305
|
+
function auditClaims(input = {}) {
|
|
11306
|
+
const claims = Array.isArray(input.claims) ? input.claims : [];
|
|
11307
|
+
const results = claims.map((claim, index) => {
|
|
11308
|
+
const evidence = claim.evidence && typeof claim.evidence === "object" ? claim.evidence : {};
|
|
11309
|
+
const verification = verifyCheck({
|
|
11310
|
+
...evidence,
|
|
11311
|
+
description: evidence.description ?? claim.claim,
|
|
11312
|
+
claim: claim.claim
|
|
11313
|
+
}, context());
|
|
11314
|
+
return {
|
|
11315
|
+
index,
|
|
11316
|
+
claim: claim.claim ?? null,
|
|
11317
|
+
status: verification.passed ? "confirmed" : "blocked",
|
|
11318
|
+
...verification
|
|
11319
|
+
};
|
|
11320
|
+
});
|
|
11321
|
+
return {
|
|
11322
|
+
passed: results.length > 0 && results.every((result) => result.passed === true),
|
|
11323
|
+
results
|
|
11324
|
+
};
|
|
11325
|
+
}
|
|
11326
|
+
return { verifyGoalOutput, auditClaims };
|
|
11327
|
+
}
|
|
11328
|
+
|
|
11329
|
+
// src/services/structured-record-store.mjs
|
|
11330
|
+
import fs3 from "node:fs";
|
|
11331
|
+
import path8 from "node:path";
|
|
11332
|
+
var DEFAULT_COLLECTION_LIMIT = 500;
|
|
11333
|
+
var COLLECTION_NAME_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
|
|
11334
|
+
function safeCollectionName(value) {
|
|
11335
|
+
const name = String(value ?? "").trim().toLowerCase();
|
|
11336
|
+
return COLLECTION_NAME_PATTERN.test(name) ? name : null;
|
|
11337
|
+
}
|
|
11338
|
+
function sessionWorkspacePath(sessionPort) {
|
|
11339
|
+
const session2 = typeof sessionPort?.current === "function" ? sessionPort.current() : null;
|
|
11340
|
+
return session2?.workspacePath ?? null;
|
|
11341
|
+
}
|
|
11342
|
+
function recordRoot(sessionPort) {
|
|
11343
|
+
const workspace = sessionWorkspacePath(sessionPort);
|
|
11344
|
+
if (!workspace) {
|
|
11345
|
+
return null;
|
|
11346
|
+
}
|
|
11347
|
+
return path8.join(workspace, "files", "tap-records");
|
|
11348
|
+
}
|
|
11349
|
+
function collectionPath(root, collection) {
|
|
11350
|
+
return path8.join(root, `${collection}.jsonl`);
|
|
11351
|
+
}
|
|
11352
|
+
function trimJsonlFile(filePath, maxRecords = DEFAULT_COLLECTION_LIMIT) {
|
|
11353
|
+
try {
|
|
11354
|
+
const lines = fs3.readFileSync(filePath, "utf8").split(/\r?\n/).filter(Boolean);
|
|
11355
|
+
if (lines.length <= maxRecords) {
|
|
11356
|
+
return;
|
|
11357
|
+
}
|
|
11358
|
+
fs3.writeFileSync(filePath, `${lines.slice(-maxRecords).join("\n")}
|
|
11359
|
+
`, "utf8");
|
|
11360
|
+
} catch {
|
|
11361
|
+
}
|
|
11362
|
+
}
|
|
11363
|
+
function createStructuredRecordStore({ sessionPort, maxRecords = DEFAULT_COLLECTION_LIMIT } = {}) {
|
|
11364
|
+
function appendRecord(collectionInput, record) {
|
|
11365
|
+
const collection = safeCollectionName(collectionInput);
|
|
11366
|
+
const root = recordRoot(sessionPort);
|
|
11367
|
+
if (!collection || !root) {
|
|
11368
|
+
return { stored: false, reason: !collection ? "invalid-collection" : "no-session-workspace" };
|
|
11369
|
+
}
|
|
11370
|
+
try {
|
|
11371
|
+
fs3.mkdirSync(root, { recursive: true });
|
|
11372
|
+
const filePath = collectionPath(root, collection);
|
|
11373
|
+
fs3.appendFileSync(filePath, `${JSON.stringify({ ...record, storedAt: (/* @__PURE__ */ new Date()).toISOString() })}
|
|
11374
|
+
`, "utf8");
|
|
11375
|
+
trimJsonlFile(filePath, maxRecords);
|
|
11376
|
+
return { stored: true, collection, path: filePath };
|
|
11377
|
+
} catch (error) {
|
|
11378
|
+
return { stored: false, collection, reason: error?.message ?? String(error ?? "unknown error") };
|
|
11379
|
+
}
|
|
11380
|
+
}
|
|
11381
|
+
function listRecords(collectionInput, options = {}) {
|
|
11382
|
+
const collection = safeCollectionName(collectionInput);
|
|
11383
|
+
const root = recordRoot(sessionPort);
|
|
11384
|
+
if (!collection || !root) {
|
|
11385
|
+
return { collection: collectionInput, records: [], available: false };
|
|
11386
|
+
}
|
|
11387
|
+
const limit = Math.max(1, Math.min(500, Math.floor(Number(options.limit ?? 50) || 50)));
|
|
11388
|
+
const filePath = collectionPath(root, collection);
|
|
11389
|
+
try {
|
|
11390
|
+
const records = fs3.readFileSync(filePath, "utf8").split(/\r?\n/).filter(Boolean).slice(-limit).map((line) => {
|
|
11391
|
+
try {
|
|
11392
|
+
return JSON.parse(line);
|
|
11393
|
+
} catch {
|
|
11394
|
+
return { parseError: true, raw: line };
|
|
11395
|
+
}
|
|
11396
|
+
});
|
|
11397
|
+
return { collection, path: filePath, available: true, records };
|
|
11398
|
+
} catch (error) {
|
|
11399
|
+
if (error?.code === "ENOENT") {
|
|
11400
|
+
return { collection, path: filePath, available: true, records: [] };
|
|
11401
|
+
}
|
|
11402
|
+
return { collection, path: filePath, available: false, records: [], error: error?.message ?? String(error) };
|
|
11403
|
+
}
|
|
11404
|
+
}
|
|
11405
|
+
function getRoot() {
|
|
11406
|
+
return recordRoot(sessionPort);
|
|
11407
|
+
}
|
|
11408
|
+
return {
|
|
11409
|
+
appendRecord,
|
|
11410
|
+
listRecords,
|
|
11411
|
+
getRoot
|
|
11412
|
+
};
|
|
11413
|
+
}
|
|
11414
|
+
|
|
10861
11415
|
// src/diagnostics/store.mjs
|
|
10862
11416
|
var DEFAULT_MAX_LOGS = 300;
|
|
10863
11417
|
var DEFAULT_MAX_EVENTS = 300;
|
|
10864
11418
|
var DEFAULT_MAX_RUNTIME_EVENTS = 300;
|
|
11419
|
+
var DEFAULT_MAX_TRACES = 300;
|
|
10865
11420
|
var MAX_STRING_LENGTH = 1200;
|
|
10866
11421
|
var MAX_COLLECTION_ITEMS = 40;
|
|
10867
11422
|
var MAX_DEPTH = 4;
|
|
@@ -10997,12 +11552,15 @@ function createDiagnosticsStore(options = {}) {
|
|
|
10997
11552
|
const logs = createRingBuffer(options.maxLogs ?? DEFAULT_MAX_LOGS);
|
|
10998
11553
|
const sessionEvents = createRingBuffer(options.maxSessionEvents ?? DEFAULT_MAX_EVENTS);
|
|
10999
11554
|
const runtimeEvents = createRingBuffer(options.maxRuntimeEvents ?? DEFAULT_MAX_RUNTIME_EVENTS);
|
|
11555
|
+
const traces = createRingBuffer(options.maxTraces ?? DEFAULT_MAX_TRACES);
|
|
11000
11556
|
const sessionEventCounts = /* @__PURE__ */ new Map();
|
|
11001
11557
|
let logCount = 0;
|
|
11002
11558
|
let runtimeEventCount = 0;
|
|
11003
11559
|
let sessionEventCount = 0;
|
|
11560
|
+
let traceCount = 0;
|
|
11004
11561
|
let cleanupSessionListener = () => {
|
|
11005
11562
|
};
|
|
11563
|
+
let recordSink = typeof options.recordSink === "function" ? options.recordSink : null;
|
|
11006
11564
|
function recordLog(source, message, options2 = {}) {
|
|
11007
11565
|
logCount += 1;
|
|
11008
11566
|
return logs.append({
|
|
@@ -11032,6 +11590,49 @@ function createDiagnosticsStore(options = {}) {
|
|
|
11032
11590
|
})
|
|
11033
11591
|
});
|
|
11034
11592
|
}
|
|
11593
|
+
function recordTrace(trace = {}) {
|
|
11594
|
+
traceCount += 1;
|
|
11595
|
+
const startedAt = trace.startedAt ?? trace.timestamp ?? nowIso();
|
|
11596
|
+
const endedAt = trace.endedAt ?? nowIso();
|
|
11597
|
+
const startMs = Date.parse(startedAt);
|
|
11598
|
+
const endMs = Date.parse(endedAt);
|
|
11599
|
+
const durationMs = Number.isFinite(startMs) && Number.isFinite(endMs) ? Math.max(0, endMs - startMs) : null;
|
|
11600
|
+
const entry = {
|
|
11601
|
+
id: createId("trace", traceCount),
|
|
11602
|
+
traceId: String(trace.traceId ?? `trace-${traceCount.toString(36)}`),
|
|
11603
|
+
timestamp: endedAt,
|
|
11604
|
+
startedAt,
|
|
11605
|
+
endedAt,
|
|
11606
|
+
durationMs,
|
|
11607
|
+
emitterId: trace.emitterId ?? trace.emitterName ?? null,
|
|
11608
|
+
emitterName: trace.emitterName ?? trace.emitterId ?? null,
|
|
11609
|
+
runIndex: Number.isFinite(Number(trace.runIndex)) ? Number(trace.runIndex) : null,
|
|
11610
|
+
emitterType: trace.emitterType ?? null,
|
|
11611
|
+
runSchedule: trace.runSchedule ?? null,
|
|
11612
|
+
status: trace.status ?? "unknown",
|
|
11613
|
+
ok: trace.ok === true,
|
|
11614
|
+
consumedRun: trace.consumedRun !== false,
|
|
11615
|
+
lineCount: Number.isFinite(Number(trace.lineCount)) ? Number(trace.lineCount) : null,
|
|
11616
|
+
droppedLineCount: Number.isFinite(Number(trace.droppedLineCount)) ? Number(trace.droppedLineCount) : null,
|
|
11617
|
+
error: trace.error ? truncateString(trace.error, MAX_STRING_LENGTH) : null,
|
|
11618
|
+
metadata: safeClone(trace.metadata ?? null, {
|
|
11619
|
+
maxDepth: 3,
|
|
11620
|
+
maxStringLength: 700,
|
|
11621
|
+
maxCollectionItems: 20
|
|
11622
|
+
}),
|
|
11623
|
+
spans: safeClone(Array.isArray(trace.spans) ? trace.spans : [], {
|
|
11624
|
+
maxDepth: 4,
|
|
11625
|
+
maxStringLength: 700,
|
|
11626
|
+
maxCollectionItems: 30
|
|
11627
|
+
})
|
|
11628
|
+
};
|
|
11629
|
+
const appended = traces.append(entry);
|
|
11630
|
+
try {
|
|
11631
|
+
recordSink?.("traces", entry);
|
|
11632
|
+
} catch {
|
|
11633
|
+
}
|
|
11634
|
+
return appended;
|
|
11635
|
+
}
|
|
11035
11636
|
function recordSessionEvent(event) {
|
|
11036
11637
|
sessionEventCount += 1;
|
|
11037
11638
|
const type = String(event?.type ?? "unknown");
|
|
@@ -11074,11 +11675,13 @@ function createDiagnosticsStore(options = {}) {
|
|
|
11074
11675
|
generatedAt: nowIso(),
|
|
11075
11676
|
logs: logs.snapshot(options2.logLimit ?? 140),
|
|
11076
11677
|
runtimeEvents: runtimeEvents.snapshot(options2.runtimeEventLimit ?? 140),
|
|
11678
|
+
traces: traces.snapshot(options2.traceLimit ?? 140),
|
|
11077
11679
|
sessionEvents: sessionEvents.snapshot(options2.sessionEventLimit ?? 140),
|
|
11078
11680
|
sessionEventCounts: Object.fromEntries([...sessionEventCounts.entries()].sort(([left], [right]) => left.localeCompare(right))),
|
|
11079
11681
|
stats: {
|
|
11080
11682
|
logs: logs.stats(),
|
|
11081
11683
|
runtimeEvents: runtimeEvents.stats(),
|
|
11684
|
+
traces: traces.stats(),
|
|
11082
11685
|
sessionEvents: sessionEvents.stats()
|
|
11083
11686
|
}
|
|
11084
11687
|
};
|
|
@@ -11086,6 +11689,10 @@ function createDiagnosticsStore(options = {}) {
|
|
|
11086
11689
|
return {
|
|
11087
11690
|
log: recordLog,
|
|
11088
11691
|
event: recordRuntimeEvent,
|
|
11692
|
+
trace: recordTrace,
|
|
11693
|
+
setRecordSink: (nextSink) => {
|
|
11694
|
+
recordSink = typeof nextSink === "function" ? nextSink : null;
|
|
11695
|
+
},
|
|
11089
11696
|
attachSession,
|
|
11090
11697
|
detachSession,
|
|
11091
11698
|
snapshot
|
|
@@ -11113,6 +11720,33 @@ function projectToolForDiagnostics(tool) {
|
|
|
11113
11720
|
overridesBuiltInTool: tool.overridesBuiltInTool === true
|
|
11114
11721
|
};
|
|
11115
11722
|
}
|
|
11723
|
+
function collectEvalSummaries(baseCwd, limit = 20) {
|
|
11724
|
+
const root = path9.join(baseCwd, "evals", "results");
|
|
11725
|
+
const summaries = [];
|
|
11726
|
+
function walk(dir) {
|
|
11727
|
+
let entries = [];
|
|
11728
|
+
try {
|
|
11729
|
+
entries = fs4.readdirSync(dir, { withFileTypes: true });
|
|
11730
|
+
} catch {
|
|
11731
|
+
return;
|
|
11732
|
+
}
|
|
11733
|
+
for (const entry of entries) {
|
|
11734
|
+
const fullPath = path9.join(dir, entry.name);
|
|
11735
|
+
if (entry.isDirectory()) {
|
|
11736
|
+
walk(fullPath);
|
|
11737
|
+
} else if (entry.name === "summary.json") {
|
|
11738
|
+
try {
|
|
11739
|
+
const stat = fs4.statSync(fullPath);
|
|
11740
|
+
const parsed = JSON.parse(fs4.readFileSync(fullPath, "utf8"));
|
|
11741
|
+
summaries.push({ path: path9.relative(baseCwd, fullPath), modifiedAt: stat.mtime.toISOString(), summary: parsed });
|
|
11742
|
+
} catch {
|
|
11743
|
+
}
|
|
11744
|
+
}
|
|
11745
|
+
}
|
|
11746
|
+
}
|
|
11747
|
+
walk(root);
|
|
11748
|
+
return summaries.sort((left, right) => new Date(right.modifiedAt) - new Date(left.modifiedAt)).slice(0, limit);
|
|
11749
|
+
}
|
|
11116
11750
|
function createTapRuntimeService(options = {}) {
|
|
11117
11751
|
const diagnosticsStore = options.diagnostics ?? createDiagnosticsStore();
|
|
11118
11752
|
const {
|
|
@@ -11125,12 +11759,18 @@ function createTapRuntimeService(options = {}) {
|
|
|
11125
11759
|
configWorkspace,
|
|
11126
11760
|
emitterWorkspace,
|
|
11127
11761
|
persist
|
|
11128
|
-
} = createRuntimeSubsystems(
|
|
11762
|
+
} = createRuntimeSubsystems({
|
|
11763
|
+
...options,
|
|
11764
|
+
diagnostics: diagnosticsStore
|
|
11765
|
+
});
|
|
11766
|
+
const recordStore = createStructuredRecordStore({ sessionPort });
|
|
11767
|
+
diagnosticsStore.setRecordSink?.((collection, record) => recordStore.appendRecord(collection, record));
|
|
11129
11768
|
const streamService = createStreamService({
|
|
11130
11769
|
streams,
|
|
11131
11770
|
configStore,
|
|
11132
11771
|
sessionPort,
|
|
11133
|
-
persist
|
|
11772
|
+
persist,
|
|
11773
|
+
recordStore
|
|
11134
11774
|
});
|
|
11135
11775
|
const emitterService = createEmitterService({
|
|
11136
11776
|
streams,
|
|
@@ -11138,6 +11778,10 @@ function createTapRuntimeService(options = {}) {
|
|
|
11138
11778
|
supervisor,
|
|
11139
11779
|
emitterWorkspace
|
|
11140
11780
|
});
|
|
11781
|
+
const goalVerificationService = createGoalVerificationService({
|
|
11782
|
+
getBaseCwd: sessionContext.getBaseCwd,
|
|
11783
|
+
getStreamHistory: (channel, limit) => streamService.getStreamHistory(channel, limit)
|
|
11784
|
+
});
|
|
11141
11785
|
const configBootstrapService = createConfigBootstrapService({
|
|
11142
11786
|
streams,
|
|
11143
11787
|
configStore,
|
|
@@ -11145,7 +11789,8 @@ function createTapRuntimeService(options = {}) {
|
|
|
11145
11789
|
sessionPort,
|
|
11146
11790
|
configWorkspace
|
|
11147
11791
|
});
|
|
11148
|
-
const { loadPersistentConfig } = configBootstrapService;
|
|
11792
|
+
const { loadPersistentConfig: loadPersistentConfigRaw } = configBootstrapService;
|
|
11793
|
+
let persistentConfigLoadPromise = null;
|
|
11149
11794
|
const sessionActivityBridge = createSessionActivityBridge({ sessionPort, supervisor });
|
|
11150
11795
|
const providerPushService = createProviderPushService({
|
|
11151
11796
|
streams,
|
|
@@ -11157,11 +11802,27 @@ function createTapRuntimeService(options = {}) {
|
|
|
11157
11802
|
const session2 = sessionPort.current();
|
|
11158
11803
|
return sessionContext.getSessionInfo(session2);
|
|
11159
11804
|
}
|
|
11805
|
+
function loadPersistentConfigOnce(inputCwd) {
|
|
11806
|
+
if (persistentConfigLoadPromise) {
|
|
11807
|
+
return persistentConfigLoadPromise;
|
|
11808
|
+
}
|
|
11809
|
+
persistentConfigLoadPromise = Promise.resolve().then(() => loadPersistentConfigRaw(inputCwd)).catch((error) => {
|
|
11810
|
+
persistentConfigLoadPromise = null;
|
|
11811
|
+
throw error;
|
|
11812
|
+
});
|
|
11813
|
+
return persistentConfigLoadPromise;
|
|
11814
|
+
}
|
|
11160
11815
|
function attachSession(session2) {
|
|
11161
11816
|
sessionContext.attachSession(session2);
|
|
11162
11817
|
sessionPort.attach(session2);
|
|
11163
11818
|
sessionActivityBridge.attach(session2);
|
|
11164
11819
|
diagnosticsStore.attachSession(session2);
|
|
11820
|
+
void loadPersistentConfigOnce(sessionContext.getBaseCwd()).then((summary) => {
|
|
11821
|
+
void sessionPort.log(summary);
|
|
11822
|
+
}).catch((error) => {
|
|
11823
|
+
const message = error?.message ?? String(error ?? "unknown error");
|
|
11824
|
+
void sessionPort.log(`Config load failed during extension attach: ${message}`, { level: "warning" });
|
|
11825
|
+
});
|
|
11165
11826
|
}
|
|
11166
11827
|
function clearNotificationsForLifecycle(options2 = {}) {
|
|
11167
11828
|
if (options2.clearNotifications === true && typeof notifications.clear === "function") {
|
|
@@ -11216,7 +11877,7 @@ function createTapRuntimeService(options = {}) {
|
|
|
11216
11877
|
} = createRuntimeHooks({
|
|
11217
11878
|
streams,
|
|
11218
11879
|
sessionPort,
|
|
11219
|
-
loadPersistentConfig,
|
|
11880
|
+
loadPersistentConfig: loadPersistentConfigOnce,
|
|
11220
11881
|
stopAllEmitters,
|
|
11221
11882
|
stopAllEmittersAndWait,
|
|
11222
11883
|
shutdownSession,
|
|
@@ -11245,8 +11906,16 @@ function createTapRuntimeService(options = {}) {
|
|
|
11245
11906
|
void sessionPort.reloadExtension();
|
|
11246
11907
|
}
|
|
11247
11908
|
};
|
|
11248
|
-
function getDiagnosticsSnapshot(options2 = {}, extra = {}) {
|
|
11909
|
+
async function getDiagnosticsSnapshot(options2 = {}, extra = {}) {
|
|
11249
11910
|
const allTools = Array.isArray(extra.tools) ? extra.tools.map(projectToolForDiagnostics) : [];
|
|
11911
|
+
let sessionRuntime = null;
|
|
11912
|
+
try {
|
|
11913
|
+
sessionRuntime = await sessionPort.getRuntimeState();
|
|
11914
|
+
} catch (error) {
|
|
11915
|
+
sessionRuntime = {
|
|
11916
|
+
error: error?.message ?? String(error ?? "unknown error")
|
|
11917
|
+
};
|
|
11918
|
+
}
|
|
11250
11919
|
return {
|
|
11251
11920
|
generatedAt: nowIso(),
|
|
11252
11921
|
session: getSessionInfo(),
|
|
@@ -11260,7 +11929,9 @@ function createTapRuntimeService(options = {}) {
|
|
|
11260
11929
|
emitters: emitterCapabilities.listEmitters(),
|
|
11261
11930
|
gateway: extra.gateway ?? null,
|
|
11262
11931
|
tools: allTools,
|
|
11932
|
+
evals: collectEvalSummaries(sessionContext.getBaseCwd(), options2.evalLimit ?? 20),
|
|
11263
11933
|
notifications: typeof notifications.snapshot === "function" ? notifications.snapshot({ limit: options2.notificationLimit ?? 20 }) : null,
|
|
11934
|
+
sessionRuntime,
|
|
11264
11935
|
diagnostics: diagnosticsStore.snapshot(options2)
|
|
11265
11936
|
};
|
|
11266
11937
|
}
|
|
@@ -11279,6 +11950,10 @@ function createTapRuntimeService(options = {}) {
|
|
|
11279
11950
|
input: canvasInput
|
|
11280
11951
|
});
|
|
11281
11952
|
},
|
|
11953
|
+
getSessionRuntimeState: () => sessionPort.getRuntimeState(),
|
|
11954
|
+
setSessionMode: (mode) => sessionPort.setMode(mode),
|
|
11955
|
+
queryRecords: (collection, options2 = {}) => recordStore.listRecords(collection, options2),
|
|
11956
|
+
getRecordRoot: () => recordStore.getRoot(),
|
|
11282
11957
|
attachSession: diagnosticsStore.attachSession,
|
|
11283
11958
|
detachSession: diagnosticsStore.detachSession
|
|
11284
11959
|
};
|
|
@@ -11286,12 +11961,14 @@ function createTapRuntimeService(options = {}) {
|
|
|
11286
11961
|
tools: {
|
|
11287
11962
|
streams: streamCapabilities,
|
|
11288
11963
|
emitters: emitterCapabilities,
|
|
11289
|
-
diagnostics: diagnosticsCapabilities
|
|
11964
|
+
diagnostics: diagnosticsCapabilities,
|
|
11965
|
+
verification: goalVerificationService
|
|
11290
11966
|
},
|
|
11291
11967
|
hooks: hookCapabilities,
|
|
11292
11968
|
session: sessionCapabilities,
|
|
11293
11969
|
provider: providerCapabilities,
|
|
11294
11970
|
diagnostics: diagnosticsCapabilities,
|
|
11971
|
+
verification: goalVerificationService,
|
|
11295
11972
|
getBaseCwd: sessionContext.getBaseCwd,
|
|
11296
11973
|
getSessionInfo,
|
|
11297
11974
|
attachSession,
|
|
@@ -11300,7 +11977,7 @@ function createTapRuntimeService(options = {}) {
|
|
|
11300
11977
|
appendStreamMessage,
|
|
11301
11978
|
getSessionStartContext,
|
|
11302
11979
|
getPromptContext,
|
|
11303
|
-
loadPersistentConfig,
|
|
11980
|
+
loadPersistentConfig: loadPersistentConfigOnce,
|
|
11304
11981
|
listEmitters: emitterCapabilities.listEmitters,
|
|
11305
11982
|
listStreams: streamCapabilities.listStreams,
|
|
11306
11983
|
postToStream: streamCapabilities.postToStream,
|
|
@@ -11341,6 +12018,7 @@ function sanitizeSnapshotOptions(input = {}) {
|
|
|
11341
12018
|
return {
|
|
11342
12019
|
streamEntryLimit: Number.isFinite(limit) ? Math.max(10, Math.min(200, Math.floor(limit))) : 80,
|
|
11343
12020
|
logLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160,
|
|
12021
|
+
traceLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160,
|
|
11344
12022
|
sessionEventLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160,
|
|
11345
12023
|
runtimeEventLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160
|
|
11346
12024
|
};
|
|
@@ -11351,8 +12029,9 @@ function summarizeSnapshot(snapshot) {
|
|
|
11351
12029
|
const streams = Array.isArray(snapshot?.streams) ? snapshot.streams.length : 0;
|
|
11352
12030
|
const providers = Array.isArray(snapshot?.gateway?.providers) ? snapshot.gateway.providers.length : 0;
|
|
11353
12031
|
const logs = snapshot?.diagnostics?.stats?.logs?.retained ?? 0;
|
|
12032
|
+
const traces = snapshot?.diagnostics?.stats?.traces?.retained ?? 0;
|
|
11354
12033
|
const sessionEvents = snapshot?.diagnostics?.stats?.sessionEvents?.retained ?? 0;
|
|
11355
|
-
return { streams, runningEmitters, configuredEmitters, providers, logs, sessionEvents };
|
|
12034
|
+
return { streams, runningEmitters, configuredEmitters, providers, logs, traces, sessionEvents };
|
|
11356
12035
|
}
|
|
11357
12036
|
function createHtml() {
|
|
11358
12037
|
return `<!doctype html>
|
|
@@ -11582,7 +12261,7 @@ function createHtml() {
|
|
|
11582
12261
|
overflow: auto;
|
|
11583
12262
|
}
|
|
11584
12263
|
|
|
11585
|
-
.stream-grid, .emitter-grid, .provider-grid {
|
|
12264
|
+
.stream-grid, .emitter-grid, .provider-grid, .trace-grid, .session-grid {
|
|
11586
12265
|
display: grid;
|
|
11587
12266
|
gap: 12px;
|
|
11588
12267
|
}
|
|
@@ -11754,6 +12433,36 @@ function createHtml() {
|
|
|
11754
12433
|
</div>
|
|
11755
12434
|
</section>
|
|
11756
12435
|
|
|
12436
|
+
<section class="panel">
|
|
12437
|
+
<div class="panel-head">
|
|
12438
|
+
<h2>Goals and traces</h2>
|
|
12439
|
+
<small id="trace-count">0 traces</small>
|
|
12440
|
+
</div>
|
|
12441
|
+
<div class="panel-body">
|
|
12442
|
+
<div class="trace-grid" id="traces"></div>
|
|
12443
|
+
</div>
|
|
12444
|
+
</section>
|
|
12445
|
+
|
|
12446
|
+
<section class="panel">
|
|
12447
|
+
<div class="panel-head">
|
|
12448
|
+
<h2>Session control</h2>
|
|
12449
|
+
<small id="session-state">unknown</small>
|
|
12450
|
+
</div>
|
|
12451
|
+
<div class="panel-body">
|
|
12452
|
+
<div class="session-grid" id="session-control"></div>
|
|
12453
|
+
</div>
|
|
12454
|
+
</section>
|
|
12455
|
+
|
|
12456
|
+
<section class="panel">
|
|
12457
|
+
<div class="panel-head">
|
|
12458
|
+
<h2>Eval gate</h2>
|
|
12459
|
+
<small id="eval-count">0 summaries</small>
|
|
12460
|
+
</div>
|
|
12461
|
+
<div class="panel-body">
|
|
12462
|
+
<div class="session-grid" id="evals"></div>
|
|
12463
|
+
</div>
|
|
12464
|
+
</section>
|
|
12465
|
+
|
|
11757
12466
|
<section class="panel timeline-panel">
|
|
11758
12467
|
<div class="panel-head">
|
|
11759
12468
|
<h2>Evidence timeline</h2>
|
|
@@ -11790,12 +12499,18 @@ function createHtml() {
|
|
|
11790
12499
|
const streams = snapshot.streams?.length ?? 0;
|
|
11791
12500
|
const providers = snapshot.gateway?.providers?.length ?? 0;
|
|
11792
12501
|
const queue = snapshot.notifications?.queueSize ?? 0;
|
|
12502
|
+
const traces = snapshot.diagnostics?.stats?.traces?.retained ?? 0;
|
|
12503
|
+
const tasks = snapshot.sessionRuntime?.tasks?.ok ? (snapshot.sessionRuntime.tasks.value?.tasks?.length ?? 0) : 0;
|
|
12504
|
+
const evals = snapshot.evals?.length ?? 0;
|
|
11793
12505
|
const sessionEvents = snapshot.diagnostics?.stats?.sessionEvents?.retained ?? 0;
|
|
11794
12506
|
el("metrics").innerHTML = [
|
|
11795
12507
|
metric("streams", streams),
|
|
11796
12508
|
metric("running emitters", running),
|
|
11797
12509
|
metric("providers", providers),
|
|
11798
12510
|
metric("queued injections", queue),
|
|
12511
|
+
metric("traces", traces),
|
|
12512
|
+
metric("tasks", tasks),
|
|
12513
|
+
metric("eval summaries", evals),
|
|
11799
12514
|
metric("configured emitters", configured),
|
|
11800
12515
|
metric("session events", sessionEvents)
|
|
11801
12516
|
].join("");
|
|
@@ -11835,6 +12550,72 @@ function createHtml() {
|
|
|
11835
12550
|
el("providers").innerHTML = gatewayRecord + rows;
|
|
11836
12551
|
}
|
|
11837
12552
|
|
|
12553
|
+
function extractMarkedJson(text, marker) {
|
|
12554
|
+
const raw = String(text ?? "");
|
|
12555
|
+
const start = "=== BEGIN_" + marker + " ===";
|
|
12556
|
+
const end = "=== END_" + marker + " ===";
|
|
12557
|
+
const startIndex = raw.indexOf(start);
|
|
12558
|
+
const endIndex = raw.indexOf(end);
|
|
12559
|
+
if (startIndex < 0 || endIndex < 0 || endIndex <= startIndex) return null;
|
|
12560
|
+
const body = raw.slice(startIndex + start.length, endIndex).trim();
|
|
12561
|
+
try { return JSON.parse(body); } catch { return null; }
|
|
12562
|
+
}
|
|
12563
|
+
|
|
12564
|
+
function collectReviewRecords(snapshot) {
|
|
12565
|
+
const records = [];
|
|
12566
|
+
for (const stream of snapshot.streams ?? []) {
|
|
12567
|
+
for (const entry of stream.entries ?? []) {
|
|
12568
|
+
const parsed = extractMarkedJson(entry.text, "REVIEW_RECORD");
|
|
12569
|
+
if (parsed) records.push({ stream: stream.name, timestamp: entry.timestamp, ...parsed });
|
|
12570
|
+
}
|
|
12571
|
+
}
|
|
12572
|
+
return records.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
12573
|
+
}
|
|
12574
|
+
|
|
12575
|
+
function renderTraces(snapshot) {
|
|
12576
|
+
const traces = snapshot.diagnostics?.traces ?? [];
|
|
12577
|
+
const goalStreams = (snapshot.streams ?? []).filter((stream) => String(stream.name ?? "").startsWith("goal-"));
|
|
12578
|
+
el("trace-count").textContent = traces.length + " traces";
|
|
12579
|
+
const traceRows = traces.slice(-12).reverse().map((trace) => {
|
|
12580
|
+
const tone = trace.ok ? "ready" : trace.status === "deferred" ? "warning" : "error";
|
|
12581
|
+
return '<div class="record"><strong>' + escapeHtml(trace.emitterName ?? trace.emitterId ?? "unknown") + ' <span class="badge ' + tone + '">' + escapeHtml(trace.status ?? "unknown") + '</span></strong><div class="meta">run=' + escapeHtml(trace.runIndex ?? "?") + ' duration=' + escapeHtml(trace.durationMs ?? "?") + 'ms | trace=' + escapeHtml(trace.traceId ?? "?") + '</div>' + (trace.error ? '<div class="details">' + escapeHtml(trace.error) + '</div>' : '') + '</div>';
|
|
12582
|
+
}).join("");
|
|
12583
|
+
const goalRows = goalStreams.map((stream) => {
|
|
12584
|
+
const latest = (stream.entries ?? []).slice(-2).reverse().map((entry) => '<div class="entry"><span class="meta">' + escapeHtml(timeOnly(entry.timestamp)) + '</span>\\n' + escapeHtml(entry.text) + '</div>').join("");
|
|
12585
|
+
return '<div class="record"><strong>' + escapeHtml(stream.name) + ' <span class="badge info">goal stream</span></strong><div class="meta">' + escapeHtml(stream.entries?.length ?? 0) + ' retained entries</div>' + (latest || '<div class="entry">No goal ledger entries retained.</div>') + '</div>';
|
|
12586
|
+
}).join("");
|
|
12587
|
+
const reviewRecords = collectReviewRecords(snapshot).slice(0, 8).map((record) => '<div class="record"><strong>' + escapeHtml(record.stream) + ' <span class="badge info">' + escapeHtml(record.issue_type ?? "review") + '</span></strong><div class="meta">entries=' + escapeHtml(record.entries_examined ?? "?") + ' | reviewed=' + escapeHtml(timeOnly(record.reviewed_at ?? record.timestamp)) + '</div><div class="details">' + escapeHtml(compactJson({ patterns_changed: record.patterns_changed ?? [], remaining_noise_delta: record.remaining_noise_delta ?? [] })) + '</div></div>').join("");
|
|
12588
|
+
el("traces").innerHTML = (traceRows || '<div class="empty">No emitter-run traces retained yet.</div>') + (goalRows ? '<div class="panel-head" style="margin:14px -14px 12px"><h2>Goal ledgers</h2><small>' + goalStreams.length + ' streams</small></div>' + goalRows : "") + (reviewRecords ? '<div class="panel-head" style="margin:14px -14px 12px"><h2>Monitor reviews</h2><small>' + collectReviewRecords(snapshot).length + ' records</small></div>' + reviewRecords : "");
|
|
12589
|
+
}
|
|
12590
|
+
|
|
12591
|
+
function renderSessionControl(snapshot) {
|
|
12592
|
+
const runtime = snapshot.sessionRuntime ?? {};
|
|
12593
|
+
const mode = runtime.mode?.ok ? runtime.mode.value : "unknown";
|
|
12594
|
+
const model = runtime.model?.ok ? runtime.model.value : null;
|
|
12595
|
+
const tasks = runtime.tasks?.ok ? (runtime.tasks.value?.tasks ?? []) : [];
|
|
12596
|
+
const schedules = runtime.schedules?.ok ? (runtime.schedules.value?.entries ?? []) : [];
|
|
12597
|
+
el("session-state").textContent = String(mode);
|
|
12598
|
+
const stateRecord = '<div class="record"><strong>session <span class="badge info">' + escapeHtml(mode) + '</span></strong><div class="meta">model=' + escapeHtml(model?.modelId ?? "unknown") + ' | reasoning=' + escapeHtml(model?.reasoningEffort ?? "default") + ' | schedules=' + escapeHtml(schedules.length) + '</div></div>';
|
|
12599
|
+
const taskRows = tasks.slice(0, 10).map((task) => '<div class="record"><strong>' + escapeHtml(task.description ?? task.id ?? "task") + ' <span class="badge ' + escapeHtml(task.status ?? "info") + '">' + escapeHtml(task.status ?? "?") + '</span></strong><div class="meta">' + escapeHtml(task.type ?? "task") + ' | id=' + escapeHtml(task.id ?? "?") + ' | mode=' + escapeHtml(task.executionMode ?? "?") + '</div></div>').join("");
|
|
12600
|
+
el("session-control").innerHTML = stateRecord + (taskRows || '<div class="empty">No native Copilot tasks are currently tracked.</div>');
|
|
12601
|
+
}
|
|
12602
|
+
|
|
12603
|
+
function renderEvals(snapshot) {
|
|
12604
|
+
const evals = snapshot.evals ?? [];
|
|
12605
|
+
el("eval-count").textContent = evals.length + " summaries";
|
|
12606
|
+
if (evals.length === 0) {
|
|
12607
|
+
el("evals").innerHTML = '<div class="empty">No eval summaries found under evals/results.</div>';
|
|
12608
|
+
return;
|
|
12609
|
+
}
|
|
12610
|
+
el("evals").innerHTML = evals.slice(0, 12).map((item) => {
|
|
12611
|
+
const summary = item.summary ?? {};
|
|
12612
|
+
const caseId = summary.caseId ?? summary.mode ?? "run";
|
|
12613
|
+
const verdict = summary.verdict?.verdict ?? (Array.isArray(summary) ? "batch" : "unknown");
|
|
12614
|
+
const tone = verdict === "pass" || summary.extensionToolAvailable === true ? "ready" : verdict === "fail" ? "error" : "info";
|
|
12615
|
+
return '<div class="record"><strong>' + escapeHtml(caseId) + ' <span class="badge ' + tone + '">' + escapeHtml(verdict) + '</span></strong><div class="meta">' + escapeHtml(item.path) + ' | ' + escapeHtml(timeOnly(item.modifiedAt)) + '</div><div class="details">' + escapeHtml(summary.verdict?.summary ?? summary.detail ?? "") + '</div></div>';
|
|
12616
|
+
}).join("");
|
|
12617
|
+
}
|
|
12618
|
+
|
|
11838
12619
|
function collectTimeline(snapshot) {
|
|
11839
12620
|
const items = [];
|
|
11840
12621
|
for (const stream of snapshot.streams ?? []) {
|
|
@@ -11851,6 +12632,9 @@ function createHtml() {
|
|
|
11851
12632
|
for (const event of snapshot.diagnostics?.runtimeEvents ?? []) {
|
|
11852
12633
|
items.push({ group: "runtime", timestamp: event.timestamp, source: event.type, level: "info", message: event.message, detail: compactJson(event.metadata) });
|
|
11853
12634
|
}
|
|
12635
|
+
for (const trace of snapshot.diagnostics?.traces ?? []) {
|
|
12636
|
+
items.push({ group: "runtime", timestamp: trace.endedAt ?? trace.timestamp, source: "trace/" + (trace.emitterName ?? trace.emitterId ?? "unknown"), level: trace.ok ? "info" : "warning", message: "emitter run " + (trace.status ?? "unknown"), detail: compactJson({ traceId: trace.traceId, runIndex: trace.runIndex, durationMs: trace.durationMs, error: trace.error }) });
|
|
12637
|
+
}
|
|
11854
12638
|
return items.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
11855
12639
|
}
|
|
11856
12640
|
|
|
@@ -11874,6 +12658,9 @@ function createHtml() {
|
|
|
11874
12658
|
renderMetrics(snapshot);
|
|
11875
12659
|
renderStreams(snapshot);
|
|
11876
12660
|
renderProviders(snapshot);
|
|
12661
|
+
renderTraces(snapshot);
|
|
12662
|
+
renderSessionControl(snapshot);
|
|
12663
|
+
renderEvals(snapshot);
|
|
11877
12664
|
renderTimeline(snapshot);
|
|
11878
12665
|
}
|
|
11879
12666
|
|
|
@@ -11922,7 +12709,7 @@ function createHtml() {
|
|
|
11922
12709
|
}
|
|
11923
12710
|
function createTapDiagnosticsCanvas({ getSnapshot, diagnostics: diagnostics2 } = {}) {
|
|
11924
12711
|
const instances = /* @__PURE__ */ new Map();
|
|
11925
|
-
function snapshot(options = {}) {
|
|
12712
|
+
async function snapshot(options = {}) {
|
|
11926
12713
|
return typeof getSnapshot === "function" ? getSnapshot(sanitizeSnapshotOptions(options)) : { generatedAt: (/* @__PURE__ */ new Date()).toISOString(), error: "No diagnostics snapshot provider configured." };
|
|
11927
12714
|
}
|
|
11928
12715
|
function log(message, level = "info", metadata = {}) {
|
|
@@ -11936,7 +12723,7 @@ function createTapDiagnosticsCanvas({ getSnapshot, diagnostics: diagnostics2 } =
|
|
|
11936
12723
|
return;
|
|
11937
12724
|
}
|
|
11938
12725
|
if (url.pathname === "/api/snapshot") {
|
|
11939
|
-
|
|
12726
|
+
void snapshot({ limit: url.searchParams.get("limit") }).then((data) => jsonResponse(res, 200, data));
|
|
11940
12727
|
return;
|
|
11941
12728
|
}
|
|
11942
12729
|
if (url.pathname === "/events") {
|
|
@@ -11947,10 +12734,17 @@ function createTapDiagnosticsCanvas({ getSnapshot, diagnostics: diagnostics2 } =
|
|
|
11947
12734
|
"X-Accel-Buffering": "no"
|
|
11948
12735
|
});
|
|
11949
12736
|
const send = () => {
|
|
11950
|
-
|
|
11951
|
-
|
|
12737
|
+
void snapshot().then((data) => {
|
|
12738
|
+
res.write(`event: snapshot
|
|
12739
|
+
data: ${JSON.stringify(data)}
|
|
12740
|
+
|
|
12741
|
+
`);
|
|
12742
|
+
}).catch((error) => {
|
|
12743
|
+
res.write(`event: snapshot
|
|
12744
|
+
data: ${JSON.stringify({ error: error?.message ?? String(error) })}
|
|
11952
12745
|
|
|
11953
12746
|
`);
|
|
12747
|
+
});
|
|
11954
12748
|
};
|
|
11955
12749
|
send();
|
|
11956
12750
|
const interval = setInterval(send, DEFAULT_REFRESH_MS);
|
|
@@ -12001,7 +12795,7 @@ data: ${JSON.stringify(snapshot())}
|
|
|
12001
12795
|
},
|
|
12002
12796
|
handler: async ({ input }) => ({
|
|
12003
12797
|
ok: true,
|
|
12004
|
-
summary: summarizeSnapshot(snapshot(input))
|
|
12798
|
+
summary: summarizeSnapshot(await snapshot(input))
|
|
12005
12799
|
})
|
|
12006
12800
|
},
|
|
12007
12801
|
{
|
|
@@ -12077,7 +12871,7 @@ function createCopilotChannelsRuntime(options = {}) {
|
|
|
12077
12871
|
log: runtimeService.provider.log
|
|
12078
12872
|
});
|
|
12079
12873
|
logRuntime("gateway created");
|
|
12080
|
-
function getDiagnosticSnapshot(options2 = {}) {
|
|
12874
|
+
async function getDiagnosticSnapshot(options2 = {}) {
|
|
12081
12875
|
return runtimeService.diagnostics.snapshot(options2, {
|
|
12082
12876
|
gateway: typeof gateway.getDiagnosticState === "function" ? gateway.getDiagnosticState() : null,
|
|
12083
12877
|
tools: gateway.isRunning() ? gateway.getAllTools(tools) : tools
|