copilot-tap-extension 2.0.6 → 2.0.8
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 +8 -3
- package/bin/install.mjs +5 -0
- package/dist/copilot-instructions.md +25 -9
- package/dist/extension.mjs +484 -30
- package/dist/skills/tap-goal/SKILL.md +127 -31
- 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/package.json +1 -1
package/dist/extension.mjs
CHANGED
|
@@ -4568,12 +4568,12 @@ function createEmitterTools(deps) {
|
|
|
4568
4568
|
properties: {
|
|
4569
4569
|
name: { type: "string", description: "Unique emitter name." },
|
|
4570
4570
|
command: { type: "string", description: "Shell command to run (creates a CommandEmitter). Output goes through EventFilter rules to determine whether lines are kept, surfaced, or injected. Use for log tailing, process monitoring, or any external command with stdout." },
|
|
4571
|
-
prompt: { type: "string", description: "Prompt to send to the agent (creates a PromptEmitter). Always injects \u2014 bypasses EventFilter entirely. Use for repeated agent tasks, status checks, or
|
|
4571
|
+
prompt: { type: "string", description: "Prompt to send to the agent (creates a PromptEmitter). Always injects \u2014 bypasses EventFilter entirely. Use for repeated agent tasks, status checks, simple messages, or autonomous goal loops." },
|
|
4572
4572
|
description: { type: "string", description: "Short summary." },
|
|
4573
4573
|
channel: { type: "string", description: "EventStream to receive accepted events." },
|
|
4574
4574
|
cwd: { type: "string", description: "Optional subdirectory relative to the session cwd. Absolute paths and paths that escape the session cwd are rejected." },
|
|
4575
|
-
every: { type: "string", description: "Optional repeat interval like 30s, 5m, 2h, or 1d (maximum about 24 days). Use 'idle' for
|
|
4576
|
-
everySchedule: { type: "array", minItems: 1, items: { type: "string" }, description: "Optional backoff schedule \u2014 an ordered non-empty list of interval strings (e.g. ['10s','20s','30s','1m','2m','5m','10m']; each maximum about 24 days). The emitter uses each interval in sequence, then repeats the last one forever. Overrides 'every' when provided. Cannot be 'idle' entries." },
|
|
4575
|
+
every: { type: "string", description: "Optional repeat interval like 30s, 5m, 2h, or 1d (maximum about 24 days). Use 'idle' for conservative prompt loops that re-run only when the session is idle. When omitted, commands run continuously and prompts run once." },
|
|
4576
|
+
everySchedule: { type: "array", minItems: 1, items: { type: "string" }, description: "Optional backoff schedule \u2014 an ordered non-empty list of interval strings (e.g. ['10s','20s','30s','1m','2m','5m','10m']; each maximum about 24 days). The emitter uses each interval in sequence, then repeats the last one forever. Overrides 'every' when provided. Cannot be 'idle' entries. Prefer this for autopilot-compatible goal loops that may need timed nudges while the session stays busy." },
|
|
4577
4577
|
lifespan: policyLifespanParameter(),
|
|
4578
4578
|
ownership: policyOwnershipParameter(),
|
|
4579
4579
|
scope: policyScopeParameter(),
|
|
@@ -4583,7 +4583,7 @@ function createEmitterTools(deps) {
|
|
|
4583
4583
|
eventFilter: EVENT_FILTER_PARAMETER_SCHEMA,
|
|
4584
4584
|
subscribe: { type: "boolean", description: "Whether to attach a session injector to the stream as part of emitter creation." },
|
|
4585
4585
|
delivery: { type: "string", description: "Session injector delivery mode. 'important'/'inject' only send inject-outcome lines, 'surface' surfaces surface outcomes and sends inject outcomes, 'all' surfaces all accepted lines while inject outcomes still push into the conversation, and 'keep'/'drop' store without proactive delivery." },
|
|
4586
|
-
maxRuns: { type: "integer", description: "Maximum number of iterations before the emitter
|
|
4586
|
+
maxRuns: { type: "integer", description: "Maximum number of real iterations before the emitter stops. Useful for idle and timed loops. For goal loops this is a budget limit, not proof that the objective is complete." },
|
|
4587
4587
|
force: policyForceParameter("emitter")
|
|
4588
4588
|
},
|
|
4589
4589
|
required: ["name"]
|
|
@@ -4654,12 +4654,29 @@ 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 canvasCount = state?.openCanvases?.ok ? state.openCanvases.value?.openCanvases?.length ?? 0 : null;
|
|
4663
|
+
return [
|
|
4664
|
+
`sessionId=${state?.sessionId ?? "(none)"}`,
|
|
4665
|
+
`mode=${typeof mode === "string" ? mode : JSON.stringify(mode)}`,
|
|
4666
|
+
model ? `model=${model.modelId ?? "unknown"} reasoning=${model.reasoningEffort ?? "default"} context=${model.contextTier ?? "default"}` : null,
|
|
4667
|
+
taskCount !== null ? `tasks=${taskCount}` : null,
|
|
4668
|
+
scheduleCount !== null ? `schedules=${scheduleCount}` : null,
|
|
4669
|
+
canvasCount !== null ? `openCanvases=${canvasCount}` : null,
|
|
4670
|
+
`elicitation=${state?.capabilities?.ui?.elicitation === true ? "available" : "unavailable"}`,
|
|
4671
|
+
`canvases=${state?.capabilities?.ui?.canvases === true ? "available" : "host-gated"}`
|
|
4672
|
+
].filter(Boolean).join("\n");
|
|
4673
|
+
}
|
|
4657
4674
|
function createDiagnosticsTools(deps) {
|
|
4658
4675
|
const diagnostics2 = deps.diagnostics ?? deps.runtime;
|
|
4659
4676
|
if (!diagnostics2 || typeof diagnostics2.openCanvas !== "function") {
|
|
4660
4677
|
return [];
|
|
4661
4678
|
}
|
|
4662
|
-
|
|
4679
|
+
const tools = [
|
|
4663
4680
|
{
|
|
4664
4681
|
name: "tap_open_diagnostics_canvas",
|
|
4665
4682
|
description: "Opens or focuses the Tap diagnostics canvas, a live flight recorder for streams, emitters, providers, logs, injection queues, and session events.",
|
|
@@ -4684,6 +4701,105 @@ function createDiagnosticsTools(deps) {
|
|
|
4684
4701
|
})
|
|
4685
4702
|
}
|
|
4686
4703
|
];
|
|
4704
|
+
if (typeof diagnostics2.getSessionRuntimeState === "function") {
|
|
4705
|
+
tools.push({
|
|
4706
|
+
name: "tap_get_session_state",
|
|
4707
|
+
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.",
|
|
4708
|
+
parameters: {
|
|
4709
|
+
type: "object",
|
|
4710
|
+
properties: {}
|
|
4711
|
+
},
|
|
4712
|
+
handler: wrapToolHandler("tap_get_session_state", {}, async () => {
|
|
4713
|
+
const state = await diagnostics2.getSessionRuntimeState();
|
|
4714
|
+
return summarizeRuntimeState(state);
|
|
4715
|
+
})
|
|
4716
|
+
});
|
|
4717
|
+
}
|
|
4718
|
+
return tools;
|
|
4719
|
+
}
|
|
4720
|
+
|
|
4721
|
+
// src/tools/goal-verification.mjs
|
|
4722
|
+
function renderVerification(prefix, result) {
|
|
4723
|
+
const lines = [
|
|
4724
|
+
`${prefix}: ${result.passed ? "passed" : "failed"}`,
|
|
4725
|
+
...result.results.map((item) => {
|
|
4726
|
+
const label = item.description ?? item.claim ?? item.path ?? item.channel ?? item.label ?? `check ${item.index}`;
|
|
4727
|
+
return `- ${item.passed ? "PASS" : "FAIL"} ${label}${item.error ? ` \u2014 ${item.error}` : ""}`;
|
|
4728
|
+
})
|
|
4729
|
+
];
|
|
4730
|
+
return lines.join("\n");
|
|
4731
|
+
}
|
|
4732
|
+
var CHECK_SCHEMA = {
|
|
4733
|
+
type: "object",
|
|
4734
|
+
properties: {
|
|
4735
|
+
type: {
|
|
4736
|
+
type: "string",
|
|
4737
|
+
description: "Check type: 'file', 'stream', or 'command_evidence'."
|
|
4738
|
+
},
|
|
4739
|
+
description: { type: "string" },
|
|
4740
|
+
path: { type: "string", description: "Workspace-relative file path for file checks." },
|
|
4741
|
+
nonEmpty: { type: "boolean", description: "For file checks, require a non-empty file." },
|
|
4742
|
+
channel: { type: "string", description: "EventStream name for stream checks." },
|
|
4743
|
+
limit: { type: "integer", description: "Recent stream entries to inspect." },
|
|
4744
|
+
minEntries: { type: "integer", description: "Minimum retained entries required." },
|
|
4745
|
+
contains: { type: "string", description: "Literal text that must be present." },
|
|
4746
|
+
pattern: { type: "string", description: "Regex pattern that must match." },
|
|
4747
|
+
label: { type: "string", description: "Human label for command evidence." },
|
|
4748
|
+
exitCode: { type: "integer", description: "Exit code from an already-run command." },
|
|
4749
|
+
success: { type: "boolean", description: "Whether already-run command evidence succeeded." }
|
|
4750
|
+
}
|
|
4751
|
+
};
|
|
4752
|
+
function createGoalVerificationTools(deps) {
|
|
4753
|
+
const verification = deps.verification ?? deps.runtime;
|
|
4754
|
+
if (!verification || typeof verification.verifyGoalOutput !== "function") {
|
|
4755
|
+
return [];
|
|
4756
|
+
}
|
|
4757
|
+
return [
|
|
4758
|
+
{
|
|
4759
|
+
name: "tap_verify_goal_output",
|
|
4760
|
+
description: "Verifies goal completion evidence without executing commands. Checks workspace files, EventStream history, or caller-supplied command evidence.",
|
|
4761
|
+
parameters: {
|
|
4762
|
+
type: "object",
|
|
4763
|
+
properties: {
|
|
4764
|
+
checks: {
|
|
4765
|
+
type: "array",
|
|
4766
|
+
items: CHECK_SCHEMA,
|
|
4767
|
+
description: "Evidence checks to perform. Command checks must use already-run command evidence; this tool does not execute shell commands."
|
|
4768
|
+
}
|
|
4769
|
+
},
|
|
4770
|
+
required: ["checks"]
|
|
4771
|
+
},
|
|
4772
|
+
handler: wrapToolHandler("tap_verify_goal_output", {}, async (args) => {
|
|
4773
|
+
const result = verification.verifyGoalOutput(args ?? {});
|
|
4774
|
+
return renderVerification("Goal output verification", result);
|
|
4775
|
+
})
|
|
4776
|
+
},
|
|
4777
|
+
{
|
|
4778
|
+
name: "tap_audit_claims",
|
|
4779
|
+
description: "Audits machine-readable goal claims against file, stream, or command-evidence surfaces before marking a goal complete.",
|
|
4780
|
+
parameters: {
|
|
4781
|
+
type: "object",
|
|
4782
|
+
properties: {
|
|
4783
|
+
claims: {
|
|
4784
|
+
type: "array",
|
|
4785
|
+
items: {
|
|
4786
|
+
type: "object",
|
|
4787
|
+
properties: {
|
|
4788
|
+
claim: { type: "string" },
|
|
4789
|
+
evidence: CHECK_SCHEMA
|
|
4790
|
+
},
|
|
4791
|
+
required: ["claim", "evidence"]
|
|
4792
|
+
}
|
|
4793
|
+
}
|
|
4794
|
+
},
|
|
4795
|
+
required: ["claims"]
|
|
4796
|
+
},
|
|
4797
|
+
handler: wrapToolHandler("tap_audit_claims", {}, async (args) => {
|
|
4798
|
+
const result = verification.auditClaims(args ?? {});
|
|
4799
|
+
return renderVerification("Claim audit", result);
|
|
4800
|
+
})
|
|
4801
|
+
}
|
|
4802
|
+
];
|
|
4687
4803
|
}
|
|
4688
4804
|
|
|
4689
4805
|
// src/tools/index.mjs
|
|
@@ -4692,10 +4808,12 @@ function createTools(deps) {
|
|
|
4692
4808
|
const streams = deps?.streams ?? source.streams ?? deps?.runtime;
|
|
4693
4809
|
const emitters = deps?.emitters ?? source.emitters ?? deps?.runtime;
|
|
4694
4810
|
const diagnostics2 = deps?.diagnostics ?? source.diagnostics ?? deps?.runtime;
|
|
4811
|
+
const verification = deps?.verification ?? source.verification ?? deps?.runtime;
|
|
4695
4812
|
return [
|
|
4696
4813
|
...createStreamTools({ streams }),
|
|
4697
4814
|
...createEmitterTools({ emitters }),
|
|
4698
|
-
...createDiagnosticsTools({ diagnostics: diagnostics2 })
|
|
4815
|
+
...createDiagnosticsTools({ diagnostics: diagnostics2 }),
|
|
4816
|
+
...createGoalVerificationTools({ verification })
|
|
4699
4817
|
];
|
|
4700
4818
|
}
|
|
4701
4819
|
|
|
@@ -8632,7 +8750,7 @@ function shouldPersistLoadedConfig(parsedConfig, normalizedConfig) {
|
|
|
8632
8750
|
return !isDeepStrictEqual(parsedConfig, serializeConfigForComparison(normalizedConfig));
|
|
8633
8751
|
}
|
|
8634
8752
|
function createConfigStore(options = {}) {
|
|
8635
|
-
const
|
|
8753
|
+
const fs3 = options.fs ?? { existsSync: existsSync2, readFileSync: readFileSync2, writeFileSync: writeFileSync2 };
|
|
8636
8754
|
const state = {
|
|
8637
8755
|
cwd: normalizeBaseCwd(options.cwd),
|
|
8638
8756
|
filePath: null,
|
|
@@ -8660,7 +8778,7 @@ function createConfigStore(options = {}) {
|
|
|
8660
8778
|
const exists = withConfigLoadPhase(
|
|
8661
8779
|
"checking config path",
|
|
8662
8780
|
"Unable to check whether the tap config file exists.",
|
|
8663
|
-
() =>
|
|
8781
|
+
() => fs3.existsSync(filePath),
|
|
8664
8782
|
{ filePath }
|
|
8665
8783
|
);
|
|
8666
8784
|
if (!exists) {
|
|
@@ -8669,7 +8787,7 @@ function createConfigStore(options = {}) {
|
|
|
8669
8787
|
const rawConfig = withConfigLoadPhase(
|
|
8670
8788
|
"reading config file",
|
|
8671
8789
|
"Unable to read the tap config file.",
|
|
8672
|
-
() =>
|
|
8790
|
+
() => fs3.readFileSync(filePath, "utf8"),
|
|
8673
8791
|
{ filePath }
|
|
8674
8792
|
);
|
|
8675
8793
|
const parsedConfig = withConfigLoadPhase(
|
|
@@ -8727,7 +8845,7 @@ function createConfigStore(options = {}) {
|
|
|
8727
8845
|
state.filePath = defaultConfigPath(state.cwd);
|
|
8728
8846
|
}
|
|
8729
8847
|
const payload = serializeConfig(state.config, LATEST_CONFIG_VERSION);
|
|
8730
|
-
|
|
8848
|
+
fs3.writeFileSync(state.filePath, `${JSON.stringify(payload, null, 2)}
|
|
8731
8849
|
`, "utf8");
|
|
8732
8850
|
}
|
|
8733
8851
|
function findStreamIndex(name) {
|
|
@@ -9003,7 +9121,7 @@ function buildCompleteMessage(state) {
|
|
|
9003
9121
|
return `Emitter '${state.name}' completed one run of ${state.emitterType} work.`;
|
|
9004
9122
|
}
|
|
9005
9123
|
if (state.maxRuns && state.runCount >= state.maxRuns) {
|
|
9006
|
-
return `Emitter '${state.name}'
|
|
9124
|
+
return `Emitter '${state.name}' reached its run budget (${state.runCount} of ${state.maxRuns} runs). This stops the emitter; goal-style loops must use their final evidence summary to decide whether the objective is complete.`;
|
|
9007
9125
|
}
|
|
9008
9126
|
return null;
|
|
9009
9127
|
}
|
|
@@ -9158,12 +9276,12 @@ function computeDeferredIterationTransition(currentState, state) {
|
|
|
9158
9276
|
};
|
|
9159
9277
|
}
|
|
9160
9278
|
function computeFailedIterationTransition(currentState, state, event, result) {
|
|
9161
|
-
if (isRunBudgetExhausted(state)) {
|
|
9162
|
-
return computeRunBudgetExhaustedTransition(currentState, state, event, result);
|
|
9163
|
-
}
|
|
9164
9279
|
if (result.deferred) {
|
|
9165
9280
|
return computeDeferredIterationTransition(currentState, state);
|
|
9166
9281
|
}
|
|
9282
|
+
if (isRunBudgetExhausted(state)) {
|
|
9283
|
+
return computeRunBudgetExhaustedTransition(currentState, state, event, result);
|
|
9284
|
+
}
|
|
9167
9285
|
if (state.runSchedule === RUN_SCHEDULE.ONE_TIME) {
|
|
9168
9286
|
return {
|
|
9169
9287
|
currentState,
|
|
@@ -9231,9 +9349,39 @@ function createDefaultProcessAdapter() {
|
|
|
9231
9349
|
readLines
|
|
9232
9350
|
};
|
|
9233
9351
|
}
|
|
9352
|
+
function recordScheduledTrace(emitter, context, { startedAt, endedAt = nowIso(), runIndex, result = null, error = null, consumedRun = true } = {}) {
|
|
9353
|
+
try {
|
|
9354
|
+
context.diagnostics?.trace?.({
|
|
9355
|
+
traceId: `${stableTraceComponent(emitter.name)}-${Number(runIndex ?? emitter.runCount) || 0}-${Date.parse(startedAt ?? endedAt) || Date.now()}`,
|
|
9356
|
+
emitterId: emitter.name,
|
|
9357
|
+
emitterName: emitter.name,
|
|
9358
|
+
runIndex: Number(runIndex ?? emitter.runCount) || null,
|
|
9359
|
+
emitterType: emitter.emitterType,
|
|
9360
|
+
runSchedule: emitter.runSchedule,
|
|
9361
|
+
startedAt: startedAt ?? endedAt,
|
|
9362
|
+
endedAt,
|
|
9363
|
+
status: result?.deferred ? "deferred" : result?.ok ? "success" : "failure",
|
|
9364
|
+
ok: result?.ok === true,
|
|
9365
|
+
consumedRun,
|
|
9366
|
+
lineCount: emitter.lineCount,
|
|
9367
|
+
droppedLineCount: emitter.droppedLineCount,
|
|
9368
|
+
error: error ?? result?.error ?? null,
|
|
9369
|
+
metadata: {
|
|
9370
|
+
every: emitter.every ?? null,
|
|
9371
|
+
everySchedule: emitter.everySchedule ?? null,
|
|
9372
|
+
maxRuns: emitter.maxRuns ?? null,
|
|
9373
|
+
stopRequested: emitter.stopRequested === true
|
|
9374
|
+
}
|
|
9375
|
+
});
|
|
9376
|
+
} catch {
|
|
9377
|
+
}
|
|
9378
|
+
}
|
|
9234
9379
|
var SESSION_ATTACH_RETRY_MS = 100;
|
|
9235
9380
|
var DEFAULT_STOP_WAIT_TIMEOUT_MS = 1e4;
|
|
9236
9381
|
var STOP_WAIT_POLL_MS = 50;
|
|
9382
|
+
function stableTraceComponent(value) {
|
|
9383
|
+
return String(value ?? "unknown").trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "unknown";
|
|
9384
|
+
}
|
|
9237
9385
|
function errorMessage(error) {
|
|
9238
9386
|
return error?.message ?? String(error ?? "unknown error");
|
|
9239
9387
|
}
|
|
@@ -9503,11 +9651,12 @@ async function runPromptIteration(emitter, context) {
|
|
|
9503
9651
|
} catch (error) {
|
|
9504
9652
|
const message = error?.message ?? String(error ?? "unknown error");
|
|
9505
9653
|
const sessionNotAttached = isSessionNotAttachedMessage(message);
|
|
9654
|
+
const deferred = sessionNotAttached || (emitter.runSchedule === RUN_SCHEDULE.TIMED || emitter.runSchedule === RUN_SCHEDULE.IDLE) && /\bsession\.idle\b/i.test(message);
|
|
9506
9655
|
return {
|
|
9507
9656
|
ok: false,
|
|
9508
9657
|
error: message,
|
|
9509
|
-
deferred
|
|
9510
|
-
consumeRun:
|
|
9658
|
+
deferred,
|
|
9659
|
+
consumeRun: deferred ? false : true,
|
|
9511
9660
|
deferredReason: sessionNotAttached ? "session-not-attached" : null
|
|
9512
9661
|
};
|
|
9513
9662
|
}
|
|
@@ -9568,6 +9717,8 @@ async function runScheduledIteration(emitter, context) {
|
|
|
9568
9717
|
emitter.status = EMITTER_STATUS.RUNNING;
|
|
9569
9718
|
emitter.runCount += 1;
|
|
9570
9719
|
emitter.lastRunAt = nowIso();
|
|
9720
|
+
const traceStartedAt = emitter.lastRunAt;
|
|
9721
|
+
const traceRunIndex = emitter.runCount;
|
|
9571
9722
|
let result;
|
|
9572
9723
|
try {
|
|
9573
9724
|
result = emitter.emitterType === EMITTER_TYPE.PROMPT ? await runPromptIteration(emitter, context) : await runCommandLoopIteration(emitter, context);
|
|
@@ -9585,6 +9736,12 @@ async function runScheduledIteration(emitter, context) {
|
|
|
9585
9736
|
if (result?.consumeRun === false) {
|
|
9586
9737
|
emitter.runCount = previousRunCount;
|
|
9587
9738
|
emitter.lastRunAt = previousLastRunAt;
|
|
9739
|
+
recordScheduledTrace(emitter, context, {
|
|
9740
|
+
startedAt: traceStartedAt,
|
|
9741
|
+
runIndex: traceRunIndex,
|
|
9742
|
+
result,
|
|
9743
|
+
consumedRun: false
|
|
9744
|
+
});
|
|
9588
9745
|
if (emitter.stopRequested) {
|
|
9589
9746
|
applyLifecycleTransition(emitter, {
|
|
9590
9747
|
type: LIFECYCLE_EVENT.ITERATION_RESULT,
|
|
@@ -9603,7 +9760,20 @@ async function runScheduledIteration(emitter, context) {
|
|
|
9603
9760
|
result,
|
|
9604
9761
|
timestamp: nowIso()
|
|
9605
9762
|
}, context);
|
|
9763
|
+
recordScheduledTrace(emitter, context, {
|
|
9764
|
+
startedAt: traceStartedAt,
|
|
9765
|
+
runIndex: traceRunIndex,
|
|
9766
|
+
result,
|
|
9767
|
+
consumedRun: true
|
|
9768
|
+
});
|
|
9606
9769
|
} catch (error) {
|
|
9770
|
+
recordScheduledTrace(emitter, context, {
|
|
9771
|
+
startedAt: traceStartedAt,
|
|
9772
|
+
runIndex: traceRunIndex,
|
|
9773
|
+
result: { ok: false, error: errorMessage(error) },
|
|
9774
|
+
error: errorMessage(error),
|
|
9775
|
+
consumedRun: true
|
|
9776
|
+
});
|
|
9607
9777
|
recordScheduledTransitionFailure(emitter, error, context);
|
|
9608
9778
|
} finally {
|
|
9609
9779
|
emitter.inFlight = false;
|
|
@@ -9617,7 +9787,8 @@ function createLifecycle({
|
|
|
9617
9787
|
sessionPort,
|
|
9618
9788
|
timerAdapter = createDefaultTimerAdapter(),
|
|
9619
9789
|
processAdapter = createDefaultProcessAdapter(),
|
|
9620
|
-
loggerAdapter = createDefaultLoggerAdapter(sessionPort)
|
|
9790
|
+
loggerAdapter = createDefaultLoggerAdapter(sessionPort),
|
|
9791
|
+
diagnostics: diagnostics2 = null
|
|
9621
9792
|
}) {
|
|
9622
9793
|
function start(emitter) {
|
|
9623
9794
|
if (emitter.runSchedule === RUN_SCHEDULE.CONTINUOUS) {
|
|
@@ -9627,6 +9798,7 @@ function createLifecycle({
|
|
|
9627
9798
|
contextStartScheduled(emitter);
|
|
9628
9799
|
}
|
|
9629
9800
|
function contextStartScheduled(emitter) {
|
|
9801
|
+
const context = { lineRouter, timerAdapter, processAdapter, loggerAdapter, sessionPort, diagnostics: diagnostics2 };
|
|
9630
9802
|
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;
|
|
9631
9803
|
lineRouter.appendSystemMessage(
|
|
9632
9804
|
emitter,
|
|
@@ -9639,17 +9811,17 @@ function createLifecycle({
|
|
|
9639
9811
|
);
|
|
9640
9812
|
if (emitter.runSchedule === RUN_SCHEDULE.IDLE) {
|
|
9641
9813
|
if (sessionPort.isIdle()) {
|
|
9642
|
-
scheduleIteration(emitter,
|
|
9814
|
+
scheduleIteration(emitter, context, IDLE_PROMPT_DELAY_MS);
|
|
9643
9815
|
}
|
|
9644
9816
|
return;
|
|
9645
9817
|
}
|
|
9646
|
-
scheduleIteration(emitter,
|
|
9818
|
+
scheduleIteration(emitter, context, 0);
|
|
9647
9819
|
}
|
|
9648
9820
|
async function stop(emitter) {
|
|
9649
9821
|
applyLifecycleTransition(
|
|
9650
9822
|
emitter,
|
|
9651
9823
|
{ type: LIFECYCLE_EVENT.STOP, timestamp: nowIso() },
|
|
9652
|
-
{ lineRouter, timerAdapter, processAdapter, loggerAdapter, sessionPort }
|
|
9824
|
+
{ lineRouter, timerAdapter, processAdapter, loggerAdapter, sessionPort, diagnostics: diagnostics2 }
|
|
9653
9825
|
);
|
|
9654
9826
|
if (isTerminalEmitterStatus(emitter.status)) {
|
|
9655
9827
|
return;
|
|
@@ -9825,14 +9997,14 @@ function restorePersistentStreamConfigBestEffort(configStore, snapshot) {
|
|
|
9825
9997
|
|
|
9826
9998
|
// src/emitter/supervisor.mjs
|
|
9827
9999
|
var ROLLBACK_STOP_WAIT_TIMEOUT_MS = 1e4;
|
|
9828
|
-
function createEmitterSupervisor({ streams, configStore, notifications, sessionPort, emitterWorkspace, persist, lifecycle: lifecycleOverride }) {
|
|
10000
|
+
function createEmitterSupervisor({ streams, configStore, notifications, sessionPort, emitterWorkspace, persist, lifecycle: lifecycleOverride, diagnostics: diagnostics2 = null }) {
|
|
9829
10001
|
const emitters = /* @__PURE__ */ new Map();
|
|
9830
10002
|
const lineRouter = createLineRouter({
|
|
9831
10003
|
streams,
|
|
9832
10004
|
notifications,
|
|
9833
10005
|
surface: (message, options) => sessionPort.log(message, options)
|
|
9834
10006
|
});
|
|
9835
|
-
const lifecycle = lifecycleOverride ?? createLifecycle({ lineRouter, sessionPort });
|
|
10007
|
+
const lifecycle = lifecycleOverride ?? createLifecycle({ lineRouter, sessionPort, diagnostics: diagnostics2 });
|
|
9836
10008
|
function hasExplicitPolicyValue(value) {
|
|
9837
10009
|
return value !== void 0 && value !== null;
|
|
9838
10010
|
}
|
|
@@ -10366,6 +10538,29 @@ function createSessionPort(initialSession = null) {
|
|
|
10366
10538
|
}
|
|
10367
10539
|
return canvasApi.open(params);
|
|
10368
10540
|
}
|
|
10541
|
+
async function getRuntimeState() {
|
|
10542
|
+
if (!session2) {
|
|
10543
|
+
throw new LifecycleError("Session is not attached; cannot inspect runtime state.");
|
|
10544
|
+
}
|
|
10545
|
+
const rpc = session2.rpc ?? {};
|
|
10546
|
+
const read = async (label, fn) => {
|
|
10547
|
+
try {
|
|
10548
|
+
return { ok: true, value: await fn() };
|
|
10549
|
+
} catch (error) {
|
|
10550
|
+
return { ok: false, error: error?.message ?? String(error ?? "unknown error") };
|
|
10551
|
+
}
|
|
10552
|
+
};
|
|
10553
|
+
return {
|
|
10554
|
+
sessionId: session2.sessionId ?? null,
|
|
10555
|
+
capabilities: session2.capabilities ?? null,
|
|
10556
|
+
mode: await read("mode", () => rpc.mode?.get?.()),
|
|
10557
|
+
model: await read("model", () => rpc.model?.getCurrent?.()),
|
|
10558
|
+
tasks: await read("tasks", () => rpc.tasks?.list?.()),
|
|
10559
|
+
schedules: await read("schedules", () => rpc.schedule?.list?.()),
|
|
10560
|
+
skills: await read("skills", () => rpc.skills?.list?.()),
|
|
10561
|
+
openCanvases: await read("openCanvases", () => rpc.canvas?.listOpen?.())
|
|
10562
|
+
};
|
|
10563
|
+
}
|
|
10369
10564
|
function registerTools(tools) {
|
|
10370
10565
|
if (!session2) return;
|
|
10371
10566
|
try {
|
|
@@ -10402,6 +10597,7 @@ function createSessionPort(initialSession = null) {
|
|
|
10402
10597
|
send,
|
|
10403
10598
|
sendAndWait,
|
|
10404
10599
|
openCanvas,
|
|
10600
|
+
getRuntimeState,
|
|
10405
10601
|
registerTools,
|
|
10406
10602
|
reloadExtension
|
|
10407
10603
|
};
|
|
@@ -10682,7 +10878,8 @@ function createRuntimeSubsystems(options = {}) {
|
|
|
10682
10878
|
notifications,
|
|
10683
10879
|
sessionPort,
|
|
10684
10880
|
emitterWorkspace,
|
|
10685
|
-
persist
|
|
10881
|
+
persist,
|
|
10882
|
+
diagnostics: options.diagnostics
|
|
10686
10883
|
});
|
|
10687
10884
|
return {
|
|
10688
10885
|
sessionContext,
|
|
@@ -10857,10 +11054,170 @@ function createStreamService(deps) {
|
|
|
10857
11054
|
};
|
|
10858
11055
|
}
|
|
10859
11056
|
|
|
11057
|
+
// src/services/goal-verification-service.mjs
|
|
11058
|
+
import fs2 from "node:fs";
|
|
11059
|
+
import path7 from "node:path";
|
|
11060
|
+
function normalizeText(value) {
|
|
11061
|
+
return String(value ?? "").trim();
|
|
11062
|
+
}
|
|
11063
|
+
function safeRegex(pattern) {
|
|
11064
|
+
try {
|
|
11065
|
+
return new RegExp(String(pattern));
|
|
11066
|
+
} catch {
|
|
11067
|
+
return null;
|
|
11068
|
+
}
|
|
11069
|
+
}
|
|
11070
|
+
function resolveWithinBase(baseCwd, requestedPath) {
|
|
11071
|
+
const raw = normalizeText(requestedPath);
|
|
11072
|
+
if (!raw) {
|
|
11073
|
+
return { ok: false, error: "path is required" };
|
|
11074
|
+
}
|
|
11075
|
+
const base = path7.resolve(baseCwd || process.cwd());
|
|
11076
|
+
const resolved = path7.isAbsolute(raw) ? path7.resolve(raw) : path7.resolve(base, raw);
|
|
11077
|
+
const relative = path7.relative(base, resolved);
|
|
11078
|
+
if (relative.startsWith("..") || path7.isAbsolute(relative)) {
|
|
11079
|
+
return { ok: false, error: `path '${raw}' is outside the session workspace` };
|
|
11080
|
+
}
|
|
11081
|
+
return { ok: true, path: resolved, displayPath: relative || "." };
|
|
11082
|
+
}
|
|
11083
|
+
function textMatches(text, check = {}) {
|
|
11084
|
+
const contains = normalizeText(check.contains);
|
|
11085
|
+
if (contains && !String(text).includes(contains)) {
|
|
11086
|
+
return { ok: false, error: `expected text to contain '${contains}'` };
|
|
11087
|
+
}
|
|
11088
|
+
const pattern = normalizeText(check.pattern);
|
|
11089
|
+
if (pattern) {
|
|
11090
|
+
const regex = safeRegex(pattern);
|
|
11091
|
+
if (!regex) {
|
|
11092
|
+
return { ok: false, error: `invalid regex '${pattern}'` };
|
|
11093
|
+
}
|
|
11094
|
+
if (!regex.test(String(text))) {
|
|
11095
|
+
return { ok: false, error: `expected text to match /${pattern}/` };
|
|
11096
|
+
}
|
|
11097
|
+
}
|
|
11098
|
+
return { ok: true };
|
|
11099
|
+
}
|
|
11100
|
+
function verifyFile(check, { baseCwd }) {
|
|
11101
|
+
const resolved = resolveWithinBase(baseCwd, check.path);
|
|
11102
|
+
if (!resolved.ok) {
|
|
11103
|
+
return { ...resolved, passed: false };
|
|
11104
|
+
}
|
|
11105
|
+
if (!fs2.existsSync(resolved.path)) {
|
|
11106
|
+
return { passed: false, path: resolved.displayPath, error: "file does not exist" };
|
|
11107
|
+
}
|
|
11108
|
+
const stat = fs2.statSync(resolved.path);
|
|
11109
|
+
if (!stat.isFile()) {
|
|
11110
|
+
return { passed: false, path: resolved.displayPath, error: "path is not a file" };
|
|
11111
|
+
}
|
|
11112
|
+
if (check.nonEmpty === true && stat.size === 0) {
|
|
11113
|
+
return { passed: false, path: resolved.displayPath, error: "file is empty" };
|
|
11114
|
+
}
|
|
11115
|
+
if (check.contains || check.pattern) {
|
|
11116
|
+
const content = fs2.readFileSync(resolved.path, "utf8");
|
|
11117
|
+
const match = textMatches(content, check);
|
|
11118
|
+
if (!match.ok) {
|
|
11119
|
+
return { passed: false, path: resolved.displayPath, error: match.error };
|
|
11120
|
+
}
|
|
11121
|
+
}
|
|
11122
|
+
return { passed: true, path: resolved.displayPath, size: stat.size };
|
|
11123
|
+
}
|
|
11124
|
+
function verifyStream(check, { getStreamHistory }) {
|
|
11125
|
+
const channel = normalizeText(check.channel);
|
|
11126
|
+
if (!channel) {
|
|
11127
|
+
return { passed: false, error: "channel is required" };
|
|
11128
|
+
}
|
|
11129
|
+
let stream;
|
|
11130
|
+
try {
|
|
11131
|
+
stream = getStreamHistory(channel, check.limit)?.stream;
|
|
11132
|
+
} catch (error) {
|
|
11133
|
+
return { passed: false, channel, error: error?.message ?? String(error) };
|
|
11134
|
+
}
|
|
11135
|
+
const entries = Array.isArray(stream?.entries) ? stream.entries : [];
|
|
11136
|
+
if (check.minEntries !== void 0 && entries.length < Number(check.minEntries)) {
|
|
11137
|
+
return { passed: false, channel, entries: entries.length, error: `expected at least ${check.minEntries} entries` };
|
|
11138
|
+
}
|
|
11139
|
+
const text = entries.map((entry) => entry.text ?? "").join("\n");
|
|
11140
|
+
const match = textMatches(text, check);
|
|
11141
|
+
if (!match.ok) {
|
|
11142
|
+
return { passed: false, channel, entries: entries.length, error: match.error };
|
|
11143
|
+
}
|
|
11144
|
+
return { passed: true, channel, entries: entries.length };
|
|
11145
|
+
}
|
|
11146
|
+
function verifyCommandEvidence(check) {
|
|
11147
|
+
const label = normalizeText(check.label ?? check.command ?? "command evidence");
|
|
11148
|
+
if (check.exitCode === void 0 && check.success !== true) {
|
|
11149
|
+
return { passed: false, label, error: "provide exitCode or success=true from an already-run command" };
|
|
11150
|
+
}
|
|
11151
|
+
const passed = check.success === true || Number(check.exitCode) === 0;
|
|
11152
|
+
return {
|
|
11153
|
+
passed,
|
|
11154
|
+
label,
|
|
11155
|
+
exitCode: check.exitCode ?? null,
|
|
11156
|
+
error: passed ? null : `command evidence did not indicate success`
|
|
11157
|
+
};
|
|
11158
|
+
}
|
|
11159
|
+
function verifyCheck(check = {}, context) {
|
|
11160
|
+
const type = normalizeText(check.type || check.kind);
|
|
11161
|
+
if (type === "file" || type === "file_exists") {
|
|
11162
|
+
return verifyFile(check, context);
|
|
11163
|
+
}
|
|
11164
|
+
if (type === "stream" || type === "stream_contains" || type === "channel") {
|
|
11165
|
+
return verifyStream(check, context);
|
|
11166
|
+
}
|
|
11167
|
+
if (type === "command" || type === "command_evidence") {
|
|
11168
|
+
return verifyCommandEvidence(check);
|
|
11169
|
+
}
|
|
11170
|
+
return { passed: false, error: `unsupported check type '${type || "<missing>"}'` };
|
|
11171
|
+
}
|
|
11172
|
+
function createGoalVerificationService({ getBaseCwd, getStreamHistory } = {}) {
|
|
11173
|
+
function context() {
|
|
11174
|
+
return {
|
|
11175
|
+
baseCwd: typeof getBaseCwd === "function" ? getBaseCwd() : process.cwd(),
|
|
11176
|
+
getStreamHistory
|
|
11177
|
+
};
|
|
11178
|
+
}
|
|
11179
|
+
function verifyGoalOutput(input = {}) {
|
|
11180
|
+
const checks = Array.isArray(input.checks) ? input.checks : [];
|
|
11181
|
+
const results = checks.map((check, index) => ({
|
|
11182
|
+
index,
|
|
11183
|
+
description: check.description ?? check.claim ?? null,
|
|
11184
|
+
type: check.type ?? check.kind ?? null,
|
|
11185
|
+
...verifyCheck(check, context())
|
|
11186
|
+
}));
|
|
11187
|
+
return {
|
|
11188
|
+
passed: results.length > 0 && results.every((result) => result.passed === true),
|
|
11189
|
+
results
|
|
11190
|
+
};
|
|
11191
|
+
}
|
|
11192
|
+
function auditClaims(input = {}) {
|
|
11193
|
+
const claims = Array.isArray(input.claims) ? input.claims : [];
|
|
11194
|
+
const results = claims.map((claim, index) => {
|
|
11195
|
+
const evidence = claim.evidence && typeof claim.evidence === "object" ? claim.evidence : {};
|
|
11196
|
+
const verification = verifyCheck({
|
|
11197
|
+
...evidence,
|
|
11198
|
+
description: evidence.description ?? claim.claim,
|
|
11199
|
+
claim: claim.claim
|
|
11200
|
+
}, context());
|
|
11201
|
+
return {
|
|
11202
|
+
index,
|
|
11203
|
+
claim: claim.claim ?? null,
|
|
11204
|
+
status: verification.passed ? "confirmed" : "blocked",
|
|
11205
|
+
...verification
|
|
11206
|
+
};
|
|
11207
|
+
});
|
|
11208
|
+
return {
|
|
11209
|
+
passed: results.length > 0 && results.every((result) => result.passed === true),
|
|
11210
|
+
results
|
|
11211
|
+
};
|
|
11212
|
+
}
|
|
11213
|
+
return { verifyGoalOutput, auditClaims };
|
|
11214
|
+
}
|
|
11215
|
+
|
|
10860
11216
|
// src/diagnostics/store.mjs
|
|
10861
11217
|
var DEFAULT_MAX_LOGS = 300;
|
|
10862
11218
|
var DEFAULT_MAX_EVENTS = 300;
|
|
10863
11219
|
var DEFAULT_MAX_RUNTIME_EVENTS = 300;
|
|
11220
|
+
var DEFAULT_MAX_TRACES = 300;
|
|
10864
11221
|
var MAX_STRING_LENGTH = 1200;
|
|
10865
11222
|
var MAX_COLLECTION_ITEMS = 40;
|
|
10866
11223
|
var MAX_DEPTH = 4;
|
|
@@ -10996,10 +11353,12 @@ function createDiagnosticsStore(options = {}) {
|
|
|
10996
11353
|
const logs = createRingBuffer(options.maxLogs ?? DEFAULT_MAX_LOGS);
|
|
10997
11354
|
const sessionEvents = createRingBuffer(options.maxSessionEvents ?? DEFAULT_MAX_EVENTS);
|
|
10998
11355
|
const runtimeEvents = createRingBuffer(options.maxRuntimeEvents ?? DEFAULT_MAX_RUNTIME_EVENTS);
|
|
11356
|
+
const traces = createRingBuffer(options.maxTraces ?? DEFAULT_MAX_TRACES);
|
|
10999
11357
|
const sessionEventCounts = /* @__PURE__ */ new Map();
|
|
11000
11358
|
let logCount = 0;
|
|
11001
11359
|
let runtimeEventCount = 0;
|
|
11002
11360
|
let sessionEventCount = 0;
|
|
11361
|
+
let traceCount = 0;
|
|
11003
11362
|
let cleanupSessionListener = () => {
|
|
11004
11363
|
};
|
|
11005
11364
|
function recordLog(source, message, options2 = {}) {
|
|
@@ -11031,6 +11390,38 @@ function createDiagnosticsStore(options = {}) {
|
|
|
11031
11390
|
})
|
|
11032
11391
|
});
|
|
11033
11392
|
}
|
|
11393
|
+
function recordTrace(trace = {}) {
|
|
11394
|
+
traceCount += 1;
|
|
11395
|
+
const startedAt = trace.startedAt ?? trace.timestamp ?? nowIso();
|
|
11396
|
+
const endedAt = trace.endedAt ?? nowIso();
|
|
11397
|
+
const startMs = Date.parse(startedAt);
|
|
11398
|
+
const endMs = Date.parse(endedAt);
|
|
11399
|
+
const durationMs = Number.isFinite(startMs) && Number.isFinite(endMs) ? Math.max(0, endMs - startMs) : null;
|
|
11400
|
+
return traces.append({
|
|
11401
|
+
id: createId("trace", traceCount),
|
|
11402
|
+
traceId: String(trace.traceId ?? `trace-${traceCount.toString(36)}`),
|
|
11403
|
+
timestamp: endedAt,
|
|
11404
|
+
startedAt,
|
|
11405
|
+
endedAt,
|
|
11406
|
+
durationMs,
|
|
11407
|
+
emitterId: trace.emitterId ?? trace.emitterName ?? null,
|
|
11408
|
+
emitterName: trace.emitterName ?? trace.emitterId ?? null,
|
|
11409
|
+
runIndex: Number.isFinite(Number(trace.runIndex)) ? Number(trace.runIndex) : null,
|
|
11410
|
+
emitterType: trace.emitterType ?? null,
|
|
11411
|
+
runSchedule: trace.runSchedule ?? null,
|
|
11412
|
+
status: trace.status ?? "unknown",
|
|
11413
|
+
ok: trace.ok === true,
|
|
11414
|
+
consumedRun: trace.consumedRun !== false,
|
|
11415
|
+
lineCount: Number.isFinite(Number(trace.lineCount)) ? Number(trace.lineCount) : null,
|
|
11416
|
+
droppedLineCount: Number.isFinite(Number(trace.droppedLineCount)) ? Number(trace.droppedLineCount) : null,
|
|
11417
|
+
error: trace.error ? truncateString(trace.error, MAX_STRING_LENGTH) : null,
|
|
11418
|
+
metadata: safeClone(trace.metadata ?? null, {
|
|
11419
|
+
maxDepth: 3,
|
|
11420
|
+
maxStringLength: 700,
|
|
11421
|
+
maxCollectionItems: 20
|
|
11422
|
+
})
|
|
11423
|
+
});
|
|
11424
|
+
}
|
|
11034
11425
|
function recordSessionEvent(event) {
|
|
11035
11426
|
sessionEventCount += 1;
|
|
11036
11427
|
const type = String(event?.type ?? "unknown");
|
|
@@ -11073,11 +11464,13 @@ function createDiagnosticsStore(options = {}) {
|
|
|
11073
11464
|
generatedAt: nowIso(),
|
|
11074
11465
|
logs: logs.snapshot(options2.logLimit ?? 140),
|
|
11075
11466
|
runtimeEvents: runtimeEvents.snapshot(options2.runtimeEventLimit ?? 140),
|
|
11467
|
+
traces: traces.snapshot(options2.traceLimit ?? 140),
|
|
11076
11468
|
sessionEvents: sessionEvents.snapshot(options2.sessionEventLimit ?? 140),
|
|
11077
11469
|
sessionEventCounts: Object.fromEntries([...sessionEventCounts.entries()].sort(([left], [right]) => left.localeCompare(right))),
|
|
11078
11470
|
stats: {
|
|
11079
11471
|
logs: logs.stats(),
|
|
11080
11472
|
runtimeEvents: runtimeEvents.stats(),
|
|
11473
|
+
traces: traces.stats(),
|
|
11081
11474
|
sessionEvents: sessionEvents.stats()
|
|
11082
11475
|
}
|
|
11083
11476
|
};
|
|
@@ -11085,6 +11478,7 @@ function createDiagnosticsStore(options = {}) {
|
|
|
11085
11478
|
return {
|
|
11086
11479
|
log: recordLog,
|
|
11087
11480
|
event: recordRuntimeEvent,
|
|
11481
|
+
trace: recordTrace,
|
|
11088
11482
|
attachSession,
|
|
11089
11483
|
detachSession,
|
|
11090
11484
|
snapshot
|
|
@@ -11124,7 +11518,10 @@ function createTapRuntimeService(options = {}) {
|
|
|
11124
11518
|
configWorkspace,
|
|
11125
11519
|
emitterWorkspace,
|
|
11126
11520
|
persist
|
|
11127
|
-
} = createRuntimeSubsystems(
|
|
11521
|
+
} = createRuntimeSubsystems({
|
|
11522
|
+
...options,
|
|
11523
|
+
diagnostics: diagnosticsStore
|
|
11524
|
+
});
|
|
11128
11525
|
const streamService = createStreamService({
|
|
11129
11526
|
streams,
|
|
11130
11527
|
configStore,
|
|
@@ -11137,6 +11534,10 @@ function createTapRuntimeService(options = {}) {
|
|
|
11137
11534
|
supervisor,
|
|
11138
11535
|
emitterWorkspace
|
|
11139
11536
|
});
|
|
11537
|
+
const goalVerificationService = createGoalVerificationService({
|
|
11538
|
+
getBaseCwd: sessionContext.getBaseCwd,
|
|
11539
|
+
getStreamHistory: (channel, limit) => streamService.getStreamHistory(channel, limit)
|
|
11540
|
+
});
|
|
11140
11541
|
const configBootstrapService = createConfigBootstrapService({
|
|
11141
11542
|
streams,
|
|
11142
11543
|
configStore,
|
|
@@ -11144,7 +11545,8 @@ function createTapRuntimeService(options = {}) {
|
|
|
11144
11545
|
sessionPort,
|
|
11145
11546
|
configWorkspace
|
|
11146
11547
|
});
|
|
11147
|
-
const { loadPersistentConfig } = configBootstrapService;
|
|
11548
|
+
const { loadPersistentConfig: loadPersistentConfigRaw } = configBootstrapService;
|
|
11549
|
+
let persistentConfigLoadPromise = null;
|
|
11148
11550
|
const sessionActivityBridge = createSessionActivityBridge({ sessionPort, supervisor });
|
|
11149
11551
|
const providerPushService = createProviderPushService({
|
|
11150
11552
|
streams,
|
|
@@ -11156,11 +11558,27 @@ function createTapRuntimeService(options = {}) {
|
|
|
11156
11558
|
const session2 = sessionPort.current();
|
|
11157
11559
|
return sessionContext.getSessionInfo(session2);
|
|
11158
11560
|
}
|
|
11561
|
+
function loadPersistentConfigOnce(inputCwd) {
|
|
11562
|
+
if (persistentConfigLoadPromise) {
|
|
11563
|
+
return persistentConfigLoadPromise;
|
|
11564
|
+
}
|
|
11565
|
+
persistentConfigLoadPromise = Promise.resolve().then(() => loadPersistentConfigRaw(inputCwd)).catch((error) => {
|
|
11566
|
+
persistentConfigLoadPromise = null;
|
|
11567
|
+
throw error;
|
|
11568
|
+
});
|
|
11569
|
+
return persistentConfigLoadPromise;
|
|
11570
|
+
}
|
|
11159
11571
|
function attachSession(session2) {
|
|
11160
11572
|
sessionContext.attachSession(session2);
|
|
11161
11573
|
sessionPort.attach(session2);
|
|
11162
11574
|
sessionActivityBridge.attach(session2);
|
|
11163
11575
|
diagnosticsStore.attachSession(session2);
|
|
11576
|
+
void loadPersistentConfigOnce(sessionContext.getBaseCwd()).then((summary) => {
|
|
11577
|
+
void sessionPort.log(summary);
|
|
11578
|
+
}).catch((error) => {
|
|
11579
|
+
const message = error?.message ?? String(error ?? "unknown error");
|
|
11580
|
+
void sessionPort.log(`Config load failed during extension attach: ${message}`, { level: "warning" });
|
|
11581
|
+
});
|
|
11164
11582
|
}
|
|
11165
11583
|
function clearNotificationsForLifecycle(options2 = {}) {
|
|
11166
11584
|
if (options2.clearNotifications === true && typeof notifications.clear === "function") {
|
|
@@ -11215,7 +11633,7 @@ function createTapRuntimeService(options = {}) {
|
|
|
11215
11633
|
} = createRuntimeHooks({
|
|
11216
11634
|
streams,
|
|
11217
11635
|
sessionPort,
|
|
11218
|
-
loadPersistentConfig,
|
|
11636
|
+
loadPersistentConfig: loadPersistentConfigOnce,
|
|
11219
11637
|
stopAllEmitters,
|
|
11220
11638
|
stopAllEmittersAndWait,
|
|
11221
11639
|
shutdownSession,
|
|
@@ -11278,6 +11696,7 @@ function createTapRuntimeService(options = {}) {
|
|
|
11278
11696
|
input: canvasInput
|
|
11279
11697
|
});
|
|
11280
11698
|
},
|
|
11699
|
+
getSessionRuntimeState: () => sessionPort.getRuntimeState(),
|
|
11281
11700
|
attachSession: diagnosticsStore.attachSession,
|
|
11282
11701
|
detachSession: diagnosticsStore.detachSession
|
|
11283
11702
|
};
|
|
@@ -11285,12 +11704,14 @@ function createTapRuntimeService(options = {}) {
|
|
|
11285
11704
|
tools: {
|
|
11286
11705
|
streams: streamCapabilities,
|
|
11287
11706
|
emitters: emitterCapabilities,
|
|
11288
|
-
diagnostics: diagnosticsCapabilities
|
|
11707
|
+
diagnostics: diagnosticsCapabilities,
|
|
11708
|
+
verification: goalVerificationService
|
|
11289
11709
|
},
|
|
11290
11710
|
hooks: hookCapabilities,
|
|
11291
11711
|
session: sessionCapabilities,
|
|
11292
11712
|
provider: providerCapabilities,
|
|
11293
11713
|
diagnostics: diagnosticsCapabilities,
|
|
11714
|
+
verification: goalVerificationService,
|
|
11294
11715
|
getBaseCwd: sessionContext.getBaseCwd,
|
|
11295
11716
|
getSessionInfo,
|
|
11296
11717
|
attachSession,
|
|
@@ -11299,7 +11720,7 @@ function createTapRuntimeService(options = {}) {
|
|
|
11299
11720
|
appendStreamMessage,
|
|
11300
11721
|
getSessionStartContext,
|
|
11301
11722
|
getPromptContext,
|
|
11302
|
-
loadPersistentConfig,
|
|
11723
|
+
loadPersistentConfig: loadPersistentConfigOnce,
|
|
11303
11724
|
listEmitters: emitterCapabilities.listEmitters,
|
|
11304
11725
|
listStreams: streamCapabilities.listStreams,
|
|
11305
11726
|
postToStream: streamCapabilities.postToStream,
|
|
@@ -11340,6 +11761,7 @@ function sanitizeSnapshotOptions(input = {}) {
|
|
|
11340
11761
|
return {
|
|
11341
11762
|
streamEntryLimit: Number.isFinite(limit) ? Math.max(10, Math.min(200, Math.floor(limit))) : 80,
|
|
11342
11763
|
logLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160,
|
|
11764
|
+
traceLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160,
|
|
11343
11765
|
sessionEventLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160,
|
|
11344
11766
|
runtimeEventLimit: Number.isFinite(limit) ? Math.max(20, Math.min(300, Math.floor(limit))) : 160
|
|
11345
11767
|
};
|
|
@@ -11350,8 +11772,9 @@ function summarizeSnapshot(snapshot) {
|
|
|
11350
11772
|
const streams = Array.isArray(snapshot?.streams) ? snapshot.streams.length : 0;
|
|
11351
11773
|
const providers = Array.isArray(snapshot?.gateway?.providers) ? snapshot.gateway.providers.length : 0;
|
|
11352
11774
|
const logs = snapshot?.diagnostics?.stats?.logs?.retained ?? 0;
|
|
11775
|
+
const traces = snapshot?.diagnostics?.stats?.traces?.retained ?? 0;
|
|
11353
11776
|
const sessionEvents = snapshot?.diagnostics?.stats?.sessionEvents?.retained ?? 0;
|
|
11354
|
-
return { streams, runningEmitters, configuredEmitters, providers, logs, sessionEvents };
|
|
11777
|
+
return { streams, runningEmitters, configuredEmitters, providers, logs, traces, sessionEvents };
|
|
11355
11778
|
}
|
|
11356
11779
|
function createHtml() {
|
|
11357
11780
|
return `<!doctype html>
|
|
@@ -11581,7 +12004,7 @@ function createHtml() {
|
|
|
11581
12004
|
overflow: auto;
|
|
11582
12005
|
}
|
|
11583
12006
|
|
|
11584
|
-
.stream-grid, .emitter-grid, .provider-grid {
|
|
12007
|
+
.stream-grid, .emitter-grid, .provider-grid, .trace-grid {
|
|
11585
12008
|
display: grid;
|
|
11586
12009
|
gap: 12px;
|
|
11587
12010
|
}
|
|
@@ -11753,6 +12176,16 @@ function createHtml() {
|
|
|
11753
12176
|
</div>
|
|
11754
12177
|
</section>
|
|
11755
12178
|
|
|
12179
|
+
<section class="panel">
|
|
12180
|
+
<div class="panel-head">
|
|
12181
|
+
<h2>Goals and traces</h2>
|
|
12182
|
+
<small id="trace-count">0 traces</small>
|
|
12183
|
+
</div>
|
|
12184
|
+
<div class="panel-body">
|
|
12185
|
+
<div class="trace-grid" id="traces"></div>
|
|
12186
|
+
</div>
|
|
12187
|
+
</section>
|
|
12188
|
+
|
|
11756
12189
|
<section class="panel timeline-panel">
|
|
11757
12190
|
<div class="panel-head">
|
|
11758
12191
|
<h2>Evidence timeline</h2>
|
|
@@ -11789,12 +12222,14 @@ function createHtml() {
|
|
|
11789
12222
|
const streams = snapshot.streams?.length ?? 0;
|
|
11790
12223
|
const providers = snapshot.gateway?.providers?.length ?? 0;
|
|
11791
12224
|
const queue = snapshot.notifications?.queueSize ?? 0;
|
|
12225
|
+
const traces = snapshot.diagnostics?.stats?.traces?.retained ?? 0;
|
|
11792
12226
|
const sessionEvents = snapshot.diagnostics?.stats?.sessionEvents?.retained ?? 0;
|
|
11793
12227
|
el("metrics").innerHTML = [
|
|
11794
12228
|
metric("streams", streams),
|
|
11795
12229
|
metric("running emitters", running),
|
|
11796
12230
|
metric("providers", providers),
|
|
11797
12231
|
metric("queued injections", queue),
|
|
12232
|
+
metric("traces", traces),
|
|
11798
12233
|
metric("configured emitters", configured),
|
|
11799
12234
|
metric("session events", sessionEvents)
|
|
11800
12235
|
].join("");
|
|
@@ -11834,6 +12269,21 @@ function createHtml() {
|
|
|
11834
12269
|
el("providers").innerHTML = gatewayRecord + rows;
|
|
11835
12270
|
}
|
|
11836
12271
|
|
|
12272
|
+
function renderTraces(snapshot) {
|
|
12273
|
+
const traces = snapshot.diagnostics?.traces ?? [];
|
|
12274
|
+
const goalStreams = (snapshot.streams ?? []).filter((stream) => String(stream.name ?? "").startsWith("goal-"));
|
|
12275
|
+
el("trace-count").textContent = traces.length + " traces";
|
|
12276
|
+
const traceRows = traces.slice(-12).reverse().map((trace) => {
|
|
12277
|
+
const tone = trace.ok ? "ready" : trace.status === "deferred" ? "warning" : "error";
|
|
12278
|
+
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>';
|
|
12279
|
+
}).join("");
|
|
12280
|
+
const goalRows = goalStreams.map((stream) => {
|
|
12281
|
+
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("");
|
|
12282
|
+
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>';
|
|
12283
|
+
}).join("");
|
|
12284
|
+
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 : "");
|
|
12285
|
+
}
|
|
12286
|
+
|
|
11837
12287
|
function collectTimeline(snapshot) {
|
|
11838
12288
|
const items = [];
|
|
11839
12289
|
for (const stream of snapshot.streams ?? []) {
|
|
@@ -11850,6 +12300,9 @@ function createHtml() {
|
|
|
11850
12300
|
for (const event of snapshot.diagnostics?.runtimeEvents ?? []) {
|
|
11851
12301
|
items.push({ group: "runtime", timestamp: event.timestamp, source: event.type, level: "info", message: event.message, detail: compactJson(event.metadata) });
|
|
11852
12302
|
}
|
|
12303
|
+
for (const trace of snapshot.diagnostics?.traces ?? []) {
|
|
12304
|
+
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 }) });
|
|
12305
|
+
}
|
|
11853
12306
|
return items.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
|
|
11854
12307
|
}
|
|
11855
12308
|
|
|
@@ -11873,6 +12326,7 @@ function createHtml() {
|
|
|
11873
12326
|
renderMetrics(snapshot);
|
|
11874
12327
|
renderStreams(snapshot);
|
|
11875
12328
|
renderProviders(snapshot);
|
|
12329
|
+
renderTraces(snapshot);
|
|
11876
12330
|
renderTimeline(snapshot);
|
|
11877
12331
|
}
|
|
11878
12332
|
|