@meetless/mla 0.1.4
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/LICENSE +201 -0
- package/README.md +81 -0
- package/dist/build-info.json +9 -0
- package/dist/bundles/ask-core.js +396 -0
- package/dist/bundles/mcp.js +16592 -0
- package/dist/bundles/trace-core.js +263 -0
- package/dist/cli.js +828 -0
- package/dist/commands/activate.js +781 -0
- package/dist/commands/adoption.js +130 -0
- package/dist/commands/ask.js +290 -0
- package/dist/commands/context.js +114 -0
- package/dist/commands/debug.js +313 -0
- package/dist/commands/doctor.js +1021 -0
- package/dist/commands/enrich.js +427 -0
- package/dist/commands/evidence.js +229 -0
- package/dist/commands/flush.js +184 -0
- package/dist/commands/graph.js +104 -0
- package/dist/commands/init.js +272 -0
- package/dist/commands/internal-active-review.js +322 -0
- package/dist/commands/internal-auto-index.js +188 -0
- package/dist/commands/internal-capture-decisions.js +320 -0
- package/dist/commands/internal-evidence-correlate.js +239 -0
- package/dist/commands/internal-evidence-hooks.js +240 -0
- package/dist/commands/internal-evidence-inject.js +231 -0
- package/dist/commands/internal-finalize.js +221 -0
- package/dist/commands/internal-pretool-observe.js +225 -0
- package/dist/commands/internal-refresh.js +136 -0
- package/dist/commands/internal-session-nudge.js +120 -0
- package/dist/commands/internal-steer-sync.js +117 -0
- package/dist/commands/internal-turn-recap.js +140 -0
- package/dist/commands/kb.js +375 -0
- package/dist/commands/kb_add.js +681 -0
- package/dist/commands/kb_forget.js +283 -0
- package/dist/commands/kb_move.js +45 -0
- package/dist/commands/kb_pending.js +410 -0
- package/dist/commands/kb_personal.js +149 -0
- package/dist/commands/kb_promote.js +188 -0
- package/dist/commands/kb_purge.js +168 -0
- package/dist/commands/kb_reingest.js +335 -0
- package/dist/commands/kb_retime.js +170 -0
- package/dist/commands/kb_review.js +391 -0
- package/dist/commands/kb_revision.js +179 -0
- package/dist/commands/kb_show.js +385 -0
- package/dist/commands/label.js +226 -0
- package/dist/commands/login.js +295 -0
- package/dist/commands/logout.js +108 -0
- package/dist/commands/mcp-supervisor.js +93 -0
- package/dist/commands/mcp.js +227 -0
- package/dist/commands/queue-prune.js +98 -0
- package/dist/commands/review.js +358 -0
- package/dist/commands/rewire.js +124 -0
- package/dist/commands/rules.js +728 -0
- package/dist/commands/scan-context.js +67 -0
- package/dist/commands/session.js +347 -0
- package/dist/commands/stats.js +479 -0
- package/dist/commands/status.js +61 -0
- package/dist/commands/summary.js +250 -0
- package/dist/commands/turn.js +114 -0
- package/dist/commands/uninstall.js +222 -0
- package/dist/commands/whoami.js +102 -0
- package/dist/commands/workspace.js +130 -0
- package/dist/hooks-template/ce0-post-tool-use.sh +34 -0
- package/dist/hooks-template/ce0-session-start.sh +49 -0
- package/dist/hooks-template/ce0-stop.sh +29 -0
- package/dist/hooks-template/ce0-user-prompt-submit.sh +38 -0
- package/dist/hooks-template/common.sh +934 -0
- package/dist/hooks-template/event-batch-filter.jq +67 -0
- package/dist/hooks-template/flush.sh +503 -0
- package/dist/hooks-template/post-tool-use.sh +423 -0
- package/dist/hooks-template/pre-tool-use.sh +69 -0
- package/dist/hooks-template/session-start.sh +140 -0
- package/dist/hooks-template/stop.sh +308 -0
- package/dist/hooks-template/user-prompt-submit.sh +1162 -0
- package/dist/lib/activation.js +79 -0
- package/dist/lib/active-conflict-cache.js +141 -0
- package/dist/lib/active-memory.js +59 -0
- package/dist/lib/active-review-runner.js +26 -0
- package/dist/lib/agent-decision/index.js +25 -0
- package/dist/lib/agent-decision/keys.js +49 -0
- package/dist/lib/agent-decision/normalize-claude.js +183 -0
- package/dist/lib/agent-decision/types.js +21 -0
- package/dist/lib/agent-decision/validate.js +216 -0
- package/dist/lib/analytics/capture.js +96 -0
- package/dist/lib/analytics/command-event.js +267 -0
- package/dist/lib/analytics/consent.js +58 -0
- package/dist/lib/analytics/coverage-gap.js +96 -0
- package/dist/lib/analytics/envelope.js +236 -0
- package/dist/lib/analytics/event-id.js +86 -0
- package/dist/lib/analytics/evidence.js +150 -0
- package/dist/lib/analytics/followthrough.js +194 -0
- package/dist/lib/analytics/forwarder.js +109 -0
- package/dist/lib/analytics/logs.js +78 -0
- package/dist/lib/analytics/metrics.js +78 -0
- package/dist/lib/analytics/recorder.js +92 -0
- package/dist/lib/analytics/review-analytics.js +75 -0
- package/dist/lib/analytics/sequence.js +77 -0
- package/dist/lib/analytics/store.js +131 -0
- package/dist/lib/analytics/turn-recap.js +279 -0
- package/dist/lib/artifact_id.js +108 -0
- package/dist/lib/auth-breaker.js +161 -0
- package/dist/lib/auto-index.js +112 -0
- package/dist/lib/classifier.js +88 -0
- package/dist/lib/config.js +298 -0
- package/dist/lib/conflict-advisory.js +64 -0
- package/dist/lib/debug-bundle.js +520 -0
- package/dist/lib/enrichment/ingest.js +301 -0
- package/dist/lib/enrichment/plan.js +253 -0
- package/dist/lib/enrichment/protocol.js +359 -0
- package/dist/lib/enrichment/scout-brief.js +176 -0
- package/dist/lib/failure-telemetry.js +444 -0
- package/dist/lib/git.js +200 -0
- package/dist/lib/governance-cache.js +77 -0
- package/dist/lib/governed-path-cache.js +76 -0
- package/dist/lib/http.js +677 -0
- package/dist/lib/identity-envelope.js +23 -0
- package/dist/lib/kb-candidate.js +65 -0
- package/dist/lib/kb_acl.js +98 -0
- package/dist/lib/login.js +353 -0
- package/dist/lib/mcp-fetchers.js +130 -0
- package/dist/lib/mcp-restart.js +47 -0
- package/dist/lib/observability.js +805 -0
- package/dist/lib/open-url.js +33 -0
- package/dist/lib/orphan-guard.js +70 -0
- package/dist/lib/packaged.js +21 -0
- package/dist/lib/reconcile-sessions.js +171 -0
- package/dist/lib/redactor.js +89 -0
- package/dist/lib/relationship-candidate-query.js +27 -0
- package/dist/lib/render.js +611 -0
- package/dist/lib/rules/applicability.js +64 -0
- package/dist/lib/rules/attest-code-rule-version.js +47 -0
- package/dist/lib/rules/attest-notes-location.js +217 -0
- package/dist/lib/rules/attest-rule-version.js +69 -0
- package/dist/lib/rules/canonical-json.js +97 -0
- package/dist/lib/rules/ce0-emit.js +64 -0
- package/dist/lib/rules/ce0-evidence.js +281 -0
- package/dist/lib/rules/ce0-recall-sample.js +82 -0
- package/dist/lib/rules/ce0-rule.js +55 -0
- package/dist/lib/rules/ce0-sampling-bucket.js +15 -0
- package/dist/lib/rules/ce0-store.js +683 -0
- package/dist/lib/rules/ce0-telemetry-project.js +93 -0
- package/dist/lib/rules/ce0-telemetry.js +158 -0
- package/dist/lib/rules/code-rule-registry.js +17 -0
- package/dist/lib/rules/command-match.js +185 -0
- package/dist/lib/rules/consult-evidence-binding.js +27 -0
- package/dist/lib/rules/consultation-capture-adapter.js +193 -0
- package/dist/lib/rules/content-match.js +56 -0
- package/dist/lib/rules/deny-admission.js +99 -0
- package/dist/lib/rules/durable-observation.js +190 -0
- package/dist/lib/rules/enforce-notes-version.js +421 -0
- package/dist/lib/rules/evaluation-input-hash.js +126 -0
- package/dist/lib/rules/evaluator.js +108 -0
- package/dist/lib/rules/inert-rule-families.js +51 -0
- package/dist/lib/rules/input-authority-resolver.js +241 -0
- package/dist/lib/rules/interception-schema.js +170 -0
- package/dist/lib/rules/interception-store.js +267 -0
- package/dist/lib/rules/live-input-authority.js +66 -0
- package/dist/lib/rules/local-matcher.js +108 -0
- package/dist/lib/rules/local-observe.js +79 -0
- package/dist/lib/rules/local-rule-version-repo.js +214 -0
- package/dist/lib/rules/memory-requirement.js +109 -0
- package/dist/lib/rules/notes-observe.js +39 -0
- package/dist/lib/rules/notes-path.js +261 -0
- package/dist/lib/rules/notes-rule.js +75 -0
- package/dist/lib/rules/observe-adapter.js +114 -0
- package/dist/lib/rules/observed-rule-hash.js +119 -0
- package/dist/lib/rules/prompt-submit-adapter.js +132 -0
- package/dist/lib/rules/requirement-subject.js +240 -0
- package/dist/lib/rules/rule-activity.js +67 -0
- package/dist/lib/rules/rule-version-hash.js +151 -0
- package/dist/lib/rules/runtime-scope.js +55 -0
- package/dist/lib/rules/stop-adapter.js +116 -0
- package/dist/lib/rules/stop-response-snapshot.js +174 -0
- package/dist/lib/rules/types.js +10 -0
- package/dist/lib/rules/ulid.js +46 -0
- package/dist/lib/rules/version-evaluation.js +156 -0
- package/dist/lib/scanner/agent-memory.js +99 -0
- package/dist/lib/scanner/bootstrap-summary.js +87 -0
- package/dist/lib/scanner/cache.js +59 -0
- package/dist/lib/scanner/frontmatter.js +42 -0
- package/dist/lib/scanner/parse-directives.js +69 -0
- package/dist/lib/scanner/parse-structured.js +72 -0
- package/dist/lib/scanner/render.js +73 -0
- package/dist/lib/scanner/scan.js +132 -0
- package/dist/lib/scanner/score.js +38 -0
- package/dist/lib/scanner/scout-mission.js +126 -0
- package/dist/lib/scanner/types.js +7 -0
- package/dist/lib/session-scope.js +195 -0
- package/dist/lib/spool.js +355 -0
- package/dist/lib/staleness.js +100 -0
- package/dist/lib/steer-cache.js +87 -0
- package/dist/lib/tagged-reference.js +20 -0
- package/dist/lib/temporal.js +109 -0
- package/dist/lib/turn-recap-emit.js +67 -0
- package/dist/lib/unwire.js +253 -0
- package/dist/lib/update-check.js +469 -0
- package/dist/lib/update-notifier.js +217 -0
- package/dist/lib/upgrade-apply.js +643 -0
- package/dist/lib/wire.js +1087 -0
- package/dist/lib/workspace.js +96 -0
- package/dist/lib/zip.js +154 -0
- package/dist/pretool-entry.js +37 -0
- package/package.json +75 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ACTIVATION_FILENAME = void 0;
|
|
37
|
+
exports.findActivation = findActivation;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
// Per-folder activation marker (opt-in capture gate). The bash counterpart
|
|
41
|
+
// lives in hooks-template/common.sh (`meetless_activated`); this module is the
|
|
42
|
+
// TypeScript side used by `mla activate` (write) and `mla doctor` (report).
|
|
43
|
+
// Both sides MUST agree on the filename and the nearest-wins walk-up semantics.
|
|
44
|
+
// "marker" here means the folder activation marker `.meetless.json`; it is the
|
|
45
|
+
// only marker concept in the CLI.
|
|
46
|
+
exports.ACTIVATION_FILENAME = ".meetless.json";
|
|
47
|
+
// Walk UP from startDir looking for the nearest `.meetless.json`, nearest-wins,
|
|
48
|
+
// mirroring how Claude Code resolves CLAUDE.md and how common.sh's
|
|
49
|
+
// `meetless_activated` gate behaves. Returns null when no marker is found.
|
|
50
|
+
function findActivation(startDir) {
|
|
51
|
+
let dir = path.resolve(startDir);
|
|
52
|
+
// eslint-disable-next-line no-constant-condition
|
|
53
|
+
while (true) {
|
|
54
|
+
const candidate = path.join(dir, exports.ACTIVATION_FILENAME);
|
|
55
|
+
if (fs.existsSync(candidate) && fs.statSync(candidate).isFile()) {
|
|
56
|
+
const found = { path: candidate, dir };
|
|
57
|
+
try {
|
|
58
|
+
const parsed = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
59
|
+
if (typeof parsed.workspaceId === "string" && parsed.workspaceId) {
|
|
60
|
+
found.workspaceId = parsed.workspaceId;
|
|
61
|
+
}
|
|
62
|
+
if (typeof parsed.workspaceName === "string" && parsed.workspaceName) {
|
|
63
|
+
found.workspaceName = parsed.workspaceName;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
catch (e) {
|
|
67
|
+
// Matches the bash gate: a malformed marker still activates the folder
|
|
68
|
+
// (the file exists); the workspaceId is simply treated as absent.
|
|
69
|
+
found.parseError = e.message;
|
|
70
|
+
}
|
|
71
|
+
return found;
|
|
72
|
+
}
|
|
73
|
+
const parent = path.dirname(dir);
|
|
74
|
+
if (parent === dir)
|
|
75
|
+
break;
|
|
76
|
+
dir = parent;
|
|
77
|
+
}
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Active cross-session conflict snapshot: the zero-network hand-off between the
|
|
3
|
+
// CLI turn-boundary sync (`_internal steer-sync`, which fetches the session's
|
|
4
|
+
// currently-open conflicts from control on the SAME pass that pulls steers) and
|
|
5
|
+
// the PreToolUse hook (which surfaces a SOFT warning when one is open).
|
|
6
|
+
//
|
|
7
|
+
// G8 / D1, notes/20260626-g8-cross-session-conflict-redesign.md §11.3 (CRITICAL-5).
|
|
8
|
+
// The snapshot is the COMPLETE current open-conflict set, overwritten each turn
|
|
9
|
+
// (never appended), so a resolved conflict simply disappears on the next sync and
|
|
10
|
+
// the warning stops automatically. The hook reads it synchronously with NO network
|
|
11
|
+
// call (same hot-path constraint as the steer cache and the governance nudge).
|
|
12
|
+
//
|
|
13
|
+
// Two deliberate properties:
|
|
14
|
+
// - The signal source is the refreshed complete snapshot, NEVER steer-injection
|
|
15
|
+
// state. A steer can be injected once and then the conflict resolves; injection
|
|
16
|
+
// state is not conflict state (§11.3 / §11.4).
|
|
17
|
+
// - A snapshot that fails to refresh (sync down) FAILS OPEN: a reader past the TTL
|
|
18
|
+
// treats it as absent (no warning) rather than a stuck warning. The warning is
|
|
19
|
+
// soft, so the safe direction on staleness is to say nothing.
|
|
20
|
+
//
|
|
21
|
+
// The file lives beside the steer cache under $MEETLESS_HOME/logs/steer/ because the
|
|
22
|
+
// same turn-boundary pass writes both; the session id is opaque
|
|
23
|
+
// (CLAUDE_CODE_SESSION_ID), used verbatim like the steer cache.
|
|
24
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
27
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
28
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
29
|
+
}
|
|
30
|
+
Object.defineProperty(o, k2, desc);
|
|
31
|
+
}) : (function(o, m, k, k2) {
|
|
32
|
+
if (k2 === undefined) k2 = k;
|
|
33
|
+
o[k2] = m[k];
|
|
34
|
+
}));
|
|
35
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
36
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
37
|
+
}) : function(o, v) {
|
|
38
|
+
o["default"] = v;
|
|
39
|
+
});
|
|
40
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
41
|
+
var ownKeys = function(o) {
|
|
42
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
43
|
+
var ar = [];
|
|
44
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
45
|
+
return ar;
|
|
46
|
+
};
|
|
47
|
+
return ownKeys(o);
|
|
48
|
+
};
|
|
49
|
+
return function (mod) {
|
|
50
|
+
if (mod && mod.__esModule) return mod;
|
|
51
|
+
var result = {};
|
|
52
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
53
|
+
__setModuleDefault(result, mod);
|
|
54
|
+
return result;
|
|
55
|
+
};
|
|
56
|
+
})();
|
|
57
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
58
|
+
exports.DEFAULT_CONFLICT_GATE_MODE = exports.ACTIVE_CONFLICT_TTL_SECONDS = void 0;
|
|
59
|
+
exports.resolveConflictGateMode = resolveConflictGateMode;
|
|
60
|
+
exports.activeConflictCachePath = activeConflictCachePath;
|
|
61
|
+
exports.writeActiveConflictCache = writeActiveConflictCache;
|
|
62
|
+
exports.readActiveConflicts = readActiveConflicts;
|
|
63
|
+
const fs = __importStar(require("fs"));
|
|
64
|
+
const path = __importStar(require("path"));
|
|
65
|
+
const config_1 = require("./config");
|
|
66
|
+
// How long a snapshot stays trusted before a reader treats it as stale and fails
|
|
67
|
+
// open. The sync rewrites it every flush (Stop hook, once per turn), so a healthy
|
|
68
|
+
// session refreshes well inside this window; a window this wide only matters when
|
|
69
|
+
// the sync is genuinely down, at which point failing open is the intended safe
|
|
70
|
+
// outcome. Generous on purpose: a single long agent turn must not false-expire a
|
|
71
|
+
// real open conflict.
|
|
72
|
+
exports.ACTIVE_CONFLICT_TTL_SECONDS = 30 * 60;
|
|
73
|
+
/** The shipped default. Soft only: a default-deny that fails closed on a stale
|
|
74
|
+
* snapshot would brick coding sessions and burn trust (§0.1, the wedge's own
|
|
75
|
+
* "soft gate before hard gate" non-negotiable). wire.ts re-exports this as the
|
|
76
|
+
* system default so flipping to hard later is a single wired change, not a rewrite. */
|
|
77
|
+
exports.DEFAULT_CONFLICT_GATE_MODE = "soft";
|
|
78
|
+
/** Resolve the gate mode from the environment, defaulting to soft. An unknown
|
|
79
|
+
* value degrades to soft (fail-safe): the only behavior that can ever block a tool
|
|
80
|
+
* is the explicit, opted-in hard mode, which is not enabled now. */
|
|
81
|
+
function resolveConflictGateMode(env = process.env) {
|
|
82
|
+
return env.MEETLESS_D1_CONFLICT_GATE === "hard" ? "hard" : exports.DEFAULT_CONFLICT_GATE_MODE;
|
|
83
|
+
}
|
|
84
|
+
function activeConflictCachePath(sessionId, home = config_1.HOME) {
|
|
85
|
+
return path.join(home, "logs", "steer", `active-conflicts-${sessionId}.json`);
|
|
86
|
+
}
|
|
87
|
+
// Best-effort: a failed cache write must never break the steer-sync hop (itself
|
|
88
|
+
// best-effort inside flush.sh). Worst case the hook keeps reading the prior
|
|
89
|
+
// snapshot until it ages past the TTL and fails open.
|
|
90
|
+
function writeActiveConflictCache(sessionId, conflicts, home = config_1.HOME, nowSeconds = Math.floor(Date.now() / 1000)) {
|
|
91
|
+
try {
|
|
92
|
+
const file = activeConflictCachePath(sessionId, home);
|
|
93
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
94
|
+
fs.writeFileSync(file, JSON.stringify({ conflicts, ts: nowSeconds }));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
/* non-fatal */
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
function coerceConflicts(raw) {
|
|
101
|
+
if (!Array.isArray(raw))
|
|
102
|
+
return [];
|
|
103
|
+
const out = [];
|
|
104
|
+
for (const item of raw) {
|
|
105
|
+
if (item &&
|
|
106
|
+
typeof item === "object" &&
|
|
107
|
+
typeof item.caseId === "string" &&
|
|
108
|
+
typeof item.openedAt === "string" &&
|
|
109
|
+
typeof item.reason === "string") {
|
|
110
|
+
const c = item;
|
|
111
|
+
out.push({ caseId: c.caseId, openedAt: c.openedAt, reason: c.reason });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return out;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Read the session's open-conflict snapshot for the PreToolUse warning. Returns []
|
|
118
|
+
* on ANY of: missing file, parse failure, malformed body, or a snapshot older than
|
|
119
|
+
* `ttlSeconds` (the fail-open staleness guard). [] means "no warning"; a non-empty
|
|
120
|
+
* result means at least one currently-open conflict. The reader never throws and
|
|
121
|
+
* never touches the network.
|
|
122
|
+
*/
|
|
123
|
+
function readActiveConflicts(sessionId, opts = {}) {
|
|
124
|
+
const home = opts.home ?? config_1.HOME;
|
|
125
|
+
const nowSeconds = opts.nowSeconds ?? Math.floor(Date.now() / 1000);
|
|
126
|
+
const ttlSeconds = opts.ttlSeconds ?? exports.ACTIVE_CONFLICT_TTL_SECONDS;
|
|
127
|
+
try {
|
|
128
|
+
const file = activeConflictCachePath(sessionId, home);
|
|
129
|
+
const body = JSON.parse(fs.readFileSync(file, "utf8"));
|
|
130
|
+
// Staleness guard: a snapshot whose ts is missing, non-numeric, or older than
|
|
131
|
+
// the TTL is treated as absent so a sync-down session fails open.
|
|
132
|
+
const ts = typeof body.ts === "number" ? body.ts : null;
|
|
133
|
+
if (ts === null || nowSeconds - ts > ttlSeconds) {
|
|
134
|
+
return [];
|
|
135
|
+
}
|
|
136
|
+
return coerceConflicts(body.conflicts);
|
|
137
|
+
}
|
|
138
|
+
catch {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.reduceActiveMemory = reduceActiveMemory;
|
|
4
|
+
exports.classifyIngestIntent = classifyIngestIntent;
|
|
5
|
+
// tools/meetless-agent/src/lib/active-memory.ts
|
|
6
|
+
// Zone 1 (Active Review) store reader. The bash PostToolUse hook appends raw
|
|
7
|
+
// records to ~/.meetless/logs/kb-knowledge.jsonl (fast, flock-guarded, no network).
|
|
8
|
+
// All dedup/TTL/debounce/caps live here, applied at READ time, so the hot path
|
|
9
|
+
// stays a single append and the policy is unit-testable in isolation.
|
|
10
|
+
// See notes/20260604-auto-propose-produced-docs-to-kb.md (active-memory store).
|
|
11
|
+
const fs_1 = require("fs");
|
|
12
|
+
const identity_envelope_1 = require("./identity-envelope");
|
|
13
|
+
// Read the append-only log and return the live, deduped, debounced, capped set of
|
|
14
|
+
// Active Review candidates. Ordering: later records win on dedup (debounce keeps
|
|
15
|
+
// the final content of a turn; identical content collapses). Returns at most
|
|
16
|
+
// maxRecords, most recent first by file order.
|
|
17
|
+
function reduceActiveMemory(file, opts) {
|
|
18
|
+
if (!(0, fs_1.existsSync)(file))
|
|
19
|
+
return [];
|
|
20
|
+
const ttlMs = opts.ttlHours * 3600 * 1000;
|
|
21
|
+
const lines = (0, fs_1.readFileSync)(file, "utf8").split("\n").filter((l) => l.trim().length > 0);
|
|
22
|
+
// Debounce within a turn: the dedup identity excludes contentHash via a separate
|
|
23
|
+
// path-key; collapse multiple edits to one canonicalPath in one (session,turn)
|
|
24
|
+
// down to the final content, then apply content-hash dedup across turns.
|
|
25
|
+
const turnKeyed = new Map();
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
let r;
|
|
28
|
+
try {
|
|
29
|
+
r = JSON.parse(line);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (r.event !== "active_memory_record")
|
|
35
|
+
continue;
|
|
36
|
+
if (opts.sessionId !== undefined && r.sessionId !== opts.sessionId)
|
|
37
|
+
continue; // scope BEFORE dedup
|
|
38
|
+
const created = Date.parse(r.createdAt);
|
|
39
|
+
if (Number.isFinite(created) && opts.nowMs - created > ttlMs)
|
|
40
|
+
continue; // TTL eviction
|
|
41
|
+
const turnPathKey = [r.workspaceId, r.repoRootHash, r.ownerUserId, r.sessionId, r.turnIndex, r.canonicalPath, r.kind].join("|");
|
|
42
|
+
turnKeyed.set(turnPathKey, r); // later edit in same turn wins (debounce)
|
|
43
|
+
}
|
|
44
|
+
// Content-hash dedup across turns: collapse identical content to one record.
|
|
45
|
+
const deduped = new Map();
|
|
46
|
+
for (const r of turnKeyed.values()) {
|
|
47
|
+
deduped.set((0, identity_envelope_1.dedupIdentity)(r), r); // later occurrence wins
|
|
48
|
+
}
|
|
49
|
+
const all = Array.from(deduped.values());
|
|
50
|
+
if (all.length <= opts.maxRecords)
|
|
51
|
+
return all;
|
|
52
|
+
return all.slice(all.length - opts.maxRecords); // keep most recent
|
|
53
|
+
}
|
|
54
|
+
function classifyIngestIntent(text) {
|
|
55
|
+
const t = text.toLowerCase();
|
|
56
|
+
if (/\b(ingest|add)\b.*\bkb\b/.test(t) || /\binto (the )?kb\b/.test(t))
|
|
57
|
+
return "kb_ingest";
|
|
58
|
+
return "active_only";
|
|
59
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runActiveReview = runActiveReview;
|
|
4
|
+
const conflict_advisory_1 = require("./conflict-advisory");
|
|
5
|
+
async function runActiveReview(args) {
|
|
6
|
+
if (args.records.length === 0) {
|
|
7
|
+
return { advisories: [], degraded: false };
|
|
8
|
+
}
|
|
9
|
+
try {
|
|
10
|
+
const resp = await args.intel.detect({ dryRun: true, candidates: args.records });
|
|
11
|
+
// Defensive: Active Review must be dry-run. A response that claims it
|
|
12
|
+
// persisted is a contract violation; drop the advisories and degrade rather
|
|
13
|
+
// than act on detections that may have mutated the graph.
|
|
14
|
+
if (resp.persisted) {
|
|
15
|
+
return { advisories: [], degraded: true };
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
advisories: (0, conflict_advisory_1.advisoriesFromDetections)(resp.detections, { minConfidence: args.minConfidence }),
|
|
19
|
+
degraded: false,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// advise-never-block (P6): any intel failure is swallowed.
|
|
24
|
+
return { advisories: [], degraded: true };
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/lib/agent-decision/index.ts
|
|
3
|
+
//
|
|
4
|
+
// Barrel for the provider-neutral agent-human decision contract. The canonical
|
|
5
|
+
// types and validator (T1) are the contract; the Claude normalizer (T2+) is the
|
|
6
|
+
// first consumer of it. Spec: notes/20260608-agent-decision-capture-design.md.
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
19
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
20
|
+
};
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
__exportStar(require("./types"), exports);
|
|
23
|
+
__exportStar(require("./validate"), exports);
|
|
24
|
+
__exportStar(require("./keys"), exports);
|
|
25
|
+
__exportStar(require("./normalize-claude"), exports);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/lib/agent-decision/keys.ts
|
|
3
|
+
//
|
|
4
|
+
// Identity + dedup keys for the canonical agent-decision contract.
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.buildEventKey = buildEventKey;
|
|
7
|
+
exports.buildClaudeProviderEventId = buildClaudeProviderEventId;
|
|
8
|
+
exports.deriveFallbackProviderEventId = deriveFallbackProviderEventId;
|
|
9
|
+
const crypto_1 = require("crypto");
|
|
10
|
+
const types_1 = require("./types");
|
|
11
|
+
// The spool/flush dedup key (spec section 5):
|
|
12
|
+
// "agent_decision_captured:<provider>:<providerEventId>"
|
|
13
|
+
// Provider-scoped, NOT hardcoded to claude_code, so a second provider's events
|
|
14
|
+
// never collide with Claude's. Control independently upserts on
|
|
15
|
+
// (workspaceId, provider, providerEventId).
|
|
16
|
+
function buildEventKey(provider, providerEventId) {
|
|
17
|
+
return `${types_1.AGENT_DECISION_EVENT}:${provider}:${providerEventId}`;
|
|
18
|
+
}
|
|
19
|
+
// Claude's stable per-question id (spec section 5): "<tool_use.id>#<questionIndex>".
|
|
20
|
+
function buildClaudeProviderEventId(toolUseId, questionIndex) {
|
|
21
|
+
return `${toolUseId}#${questionIndex}`;
|
|
22
|
+
}
|
|
23
|
+
// NUL field separator so concatenation is unambiguous: ("a","bc") can never
|
|
24
|
+
// collide with ("ab","c"). NUL cannot appear in JSON text or a session id, so it
|
|
25
|
+
// is a safe fence (a plain space is not, since labels contain spaces).
|
|
26
|
+
const FIELD_SEP = "\u0000";
|
|
27
|
+
// Deterministic fallback providerEventId for a provider with NO stable per-event
|
|
28
|
+
// id (spec section 7, INV-STABLE-FALLBACK-ID):
|
|
29
|
+
// sha256(provider + providerSessionId + sourceOrdinal
|
|
30
|
+
// + normalizedPrompt + normalizedChoices + normalizedAnswer)
|
|
31
|
+
// CRITICAL: it must NOT depend on capture timestamp. occurredAt drifts between
|
|
32
|
+
// the real-time path and the transcript-scan backstop, so feeding it in would
|
|
33
|
+
// break dedup across the two paths. It is therefore not an input here. Claude
|
|
34
|
+
// uses the stable "<tool_use.id>#<i>" id and never needs this.
|
|
35
|
+
function deriveFallbackProviderEventId(input) {
|
|
36
|
+
const normalizedPrompt = JSON.stringify({ title: input.prompt.title, body: input.prompt.body });
|
|
37
|
+
// Order is significant (positional choice ids), so preserve it.
|
|
38
|
+
const normalizedChoices = JSON.stringify(input.choices.map((c) => [c.id, c.label, c.description ?? ""]));
|
|
39
|
+
const normalizedAnswer = JSON.stringify({ type: input.answer.type, value: input.answer.value });
|
|
40
|
+
const material = [
|
|
41
|
+
input.provider,
|
|
42
|
+
input.providerSessionId,
|
|
43
|
+
input.sourceOrdinal,
|
|
44
|
+
normalizedPrompt,
|
|
45
|
+
normalizedChoices,
|
|
46
|
+
normalizedAnswer,
|
|
47
|
+
].join(FIELD_SEP);
|
|
48
|
+
return (0, crypto_1.createHash)("sha256").update(material, "utf8").digest("hex");
|
|
49
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/lib/agent-decision/normalize-claude.ts
|
|
3
|
+
//
|
|
4
|
+
// The ONE normalization seam, Claude Code AskUserQuestion edition (T2-T5).
|
|
5
|
+
//
|
|
6
|
+
// Pure functions: raw AskUserQuestion (tool_input + tool_response) -> canonical
|
|
7
|
+
// decisions. A single tool call carrying N questions decomposes into N decisions
|
|
8
|
+
// (spec section 5), each with providerEventId "<tool_use.id>#<i>". Everything
|
|
9
|
+
// Claude-specific is confined here and to rawProviderPayload; the output is
|
|
10
|
+
// pure canonical contract (INV-ADAPTER-BOUNDARY, INV-NORMALIZATION).
|
|
11
|
+
//
|
|
12
|
+
// When a real second provider lands, it gets its own normalize-<provider>.ts that
|
|
13
|
+
// emits the same CanonicalDecisionPayload; nothing downstream changes.
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.CLAUDE_TOOL_NAME = exports.CLAUDE_PROVIDER_SOURCE = exports.CLAUDE_PROVIDER = void 0;
|
|
16
|
+
exports.normalizeClaudeAskUserQuestion = normalizeClaudeAskUserQuestion;
|
|
17
|
+
const keys_1 = require("./keys");
|
|
18
|
+
exports.CLAUDE_PROVIDER = "claude_code";
|
|
19
|
+
exports.CLAUDE_PROVIDER_SOURCE = "claude_hook";
|
|
20
|
+
exports.CLAUDE_TOOL_NAME = "AskUserQuestion";
|
|
21
|
+
// Delimiters a multiSelect answer might use IF Claude serializes it as a string.
|
|
22
|
+
// Claude's real multi-select serialization is UNVERIFIED (spec section 3); the
|
|
23
|
+
// adapter tolerates an array (preferred) or any of these delimiters.
|
|
24
|
+
const MULTI_DELIMITERS = /\r?\n|,|\||;/;
|
|
25
|
+
function toChoices(options) {
|
|
26
|
+
return (options ?? []).map((o, idx) => {
|
|
27
|
+
const choice = { id: `choice_${idx}`, label: String(o.label ?? "") };
|
|
28
|
+
if (o.description !== undefined)
|
|
29
|
+
choice.description = String(o.description);
|
|
30
|
+
return choice;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
// Match one answer string against the offered labels. Exact match first
|
|
34
|
+
// (INV-CHOICE-ID resolution, spec section 6).
|
|
35
|
+
function matchLabel(value, choices) {
|
|
36
|
+
const matches = choices.filter((c) => c.label === value);
|
|
37
|
+
if (matches.length === 1)
|
|
38
|
+
return { choiceId: matches[0].id, status: "exact_unique" };
|
|
39
|
+
if (matches.length > 1)
|
|
40
|
+
return { choiceId: matches[0].id, status: "exact_ambiguous" };
|
|
41
|
+
return { status: "no_match" };
|
|
42
|
+
}
|
|
43
|
+
// One leading tolerated delimiter (plus any trailing whitespace) at the start of
|
|
44
|
+
// a string. Used to step past the separator between two offered labels.
|
|
45
|
+
const LEADING_DELIM = /^(?:\r?\n|[,|;])\s*/;
|
|
46
|
+
// A delimited multiSelect string can hide a delimiter INSIDE a label (e.g. an
|
|
47
|
+
// option literally named "Ship now, with a flag"). A naive split would shred that
|
|
48
|
+
// label and silently misreport what the human picked, which is an audit-integrity
|
|
49
|
+
// corruption in a governance product. So first try to read the whole string as a
|
|
50
|
+
// delimiter-joined run of offered labels, longest-label-first; return null (so the
|
|
51
|
+
// caller naive-splits) only when the string is NOT fully explained by offered
|
|
52
|
+
// labels. This also subsumes the whole-string-is-one-label case.
|
|
53
|
+
function matchOfferedLabelSequence(raw, choices) {
|
|
54
|
+
const labels = choices
|
|
55
|
+
.map((c) => c.label)
|
|
56
|
+
.filter((l) => l.length > 0)
|
|
57
|
+
.sort((a, b) => b.length - a.length);
|
|
58
|
+
if (labels.length === 0)
|
|
59
|
+
return null;
|
|
60
|
+
const found = [];
|
|
61
|
+
let rest = raw.trim();
|
|
62
|
+
while (rest.length > 0) {
|
|
63
|
+
const label = labels.find((l) => rest === l || (rest.startsWith(l) && LEADING_DELIM.test(rest.slice(l.length))));
|
|
64
|
+
if (label === undefined)
|
|
65
|
+
return null; // not a clean label run; caller naive-splits
|
|
66
|
+
found.push(label);
|
|
67
|
+
rest = rest.slice(label.length).replace(LEADING_DELIM, "").trim();
|
|
68
|
+
}
|
|
69
|
+
return found.length > 0 ? found : null;
|
|
70
|
+
}
|
|
71
|
+
// Coerce a raw multiSelect answer into an array of label strings, tolerating
|
|
72
|
+
// either a real array (preferred) or a delimited string.
|
|
73
|
+
function toMultiValues(raw, choices) {
|
|
74
|
+
if (Array.isArray(raw))
|
|
75
|
+
return raw.map((x) => String(x));
|
|
76
|
+
if (typeof raw !== "string")
|
|
77
|
+
return raw == null ? [] : [String(raw)];
|
|
78
|
+
// Prefer an exact decomposition into offered labels (handles a label that itself
|
|
79
|
+
// contains a delimiter); only naive-split when that fails.
|
|
80
|
+
const asLabels = matchOfferedLabelSequence(raw, choices);
|
|
81
|
+
if (asLabels)
|
|
82
|
+
return asLabels;
|
|
83
|
+
return raw
|
|
84
|
+
.split(MULTI_DELIMITERS)
|
|
85
|
+
.map((s) => s.trim())
|
|
86
|
+
.filter((s) => s.length > 0);
|
|
87
|
+
}
|
|
88
|
+
function buildAnswer(question, rawAnswer, choices) {
|
|
89
|
+
const multiSelect = question.multiSelect === true;
|
|
90
|
+
if (multiSelect) {
|
|
91
|
+
const values = toMultiValues(rawAnswer, choices);
|
|
92
|
+
const matchedIds = [];
|
|
93
|
+
let anyMatch = false;
|
|
94
|
+
let anyAmbiguous = false;
|
|
95
|
+
for (const v of values) {
|
|
96
|
+
const m = matchLabel(v, choices);
|
|
97
|
+
if (m.status === "no_match")
|
|
98
|
+
continue;
|
|
99
|
+
anyMatch = true;
|
|
100
|
+
if (m.status === "exact_ambiguous")
|
|
101
|
+
anyAmbiguous = true;
|
|
102
|
+
if (m.choiceId)
|
|
103
|
+
matchedIds.push(m.choiceId);
|
|
104
|
+
}
|
|
105
|
+
const status = !anyMatch ? "no_match" : anyAmbiguous ? "exact_ambiguous" : "exact_unique";
|
|
106
|
+
const answer = {
|
|
107
|
+
type: "multi_choice_labels",
|
|
108
|
+
value: values,
|
|
109
|
+
choiceMatchStatus: status,
|
|
110
|
+
raw: rawAnswer,
|
|
111
|
+
};
|
|
112
|
+
if (matchedIds.length > 0)
|
|
113
|
+
answer.choiceIds = matchedIds;
|
|
114
|
+
return answer;
|
|
115
|
+
}
|
|
116
|
+
// Single-select. Claude returns the chosen option label, OR free text the user
|
|
117
|
+
// typed for "Other". Match by exact label.
|
|
118
|
+
const value = typeof rawAnswer === "string" ? rawAnswer : String(rawAnswer ?? "");
|
|
119
|
+
const m = matchLabel(value, choices);
|
|
120
|
+
if (m.status === "no_match") {
|
|
121
|
+
return { type: "free_text", value, choiceMatchStatus: "no_match", raw: rawAnswer };
|
|
122
|
+
}
|
|
123
|
+
// Known, unfixable limitation (spec section 6): if the user typed "Other" text
|
|
124
|
+
// that exactly equals an offered label, this records it as a selection. Claude
|
|
125
|
+
// does not label answer origin, so the adapter cannot tell them apart.
|
|
126
|
+
return {
|
|
127
|
+
type: "choice_label",
|
|
128
|
+
value,
|
|
129
|
+
choiceId: m.choiceId,
|
|
130
|
+
choiceMatchStatus: m.status,
|
|
131
|
+
raw: rawAnswer,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
function deriveDecisionKind(question, answer) {
|
|
135
|
+
if (question.multiSelect === true)
|
|
136
|
+
return "multi_choice";
|
|
137
|
+
if (answer.type === "free_text")
|
|
138
|
+
return "free_text";
|
|
139
|
+
return "choice";
|
|
140
|
+
}
|
|
141
|
+
// Decompose one AskUserQuestion call into N canonical decisions, one per question.
|
|
142
|
+
// Questions with no entry in the answers map are skipped: a question that was not
|
|
143
|
+
// answered is not a captured human decision.
|
|
144
|
+
function normalizeClaudeAskUserQuestion(raw, ctx) {
|
|
145
|
+
const out = [];
|
|
146
|
+
const questions = Array.isArray(raw.questions) ? raw.questions : [];
|
|
147
|
+
questions.forEach((question, i) => {
|
|
148
|
+
const key = question.question;
|
|
149
|
+
if (typeof key !== "string")
|
|
150
|
+
return;
|
|
151
|
+
if (!(key in (raw.answers ?? {})))
|
|
152
|
+
return; // unanswered -> not a decision
|
|
153
|
+
const rawAnswer = raw.answers[key];
|
|
154
|
+
const choices = toChoices(question.options);
|
|
155
|
+
const answer = buildAnswer(question, rawAnswer, choices);
|
|
156
|
+
const decisionKind = deriveDecisionKind(question, answer);
|
|
157
|
+
const payload = {
|
|
158
|
+
provider: exports.CLAUDE_PROVIDER,
|
|
159
|
+
providerSource: exports.CLAUDE_PROVIDER_SOURCE,
|
|
160
|
+
providerToolName: exports.CLAUDE_TOOL_NAME,
|
|
161
|
+
providerEventId: (0, keys_1.buildClaudeProviderEventId)(raw.toolUseId, i),
|
|
162
|
+
providerSessionId: ctx.providerSessionId,
|
|
163
|
+
decisionKind,
|
|
164
|
+
prompt: {
|
|
165
|
+
title: typeof question.header === "string" && question.header.trim().length > 0 ? question.header : key,
|
|
166
|
+
body: key,
|
|
167
|
+
},
|
|
168
|
+
choices,
|
|
169
|
+
answer,
|
|
170
|
+
multiSelect: question.multiSelect === true,
|
|
171
|
+
turnIndex: ctx.turnIndex ?? null,
|
|
172
|
+
traceId: ctx.traceId ?? null,
|
|
173
|
+
capturedBy: ctx.capturedBy,
|
|
174
|
+
actorDisplayName: ctx.actorDisplayName ?? null,
|
|
175
|
+
// Audit: preserve exactly what the provider gave us for this question.
|
|
176
|
+
rawProviderPayload: { question, answer: rawAnswer },
|
|
177
|
+
};
|
|
178
|
+
if (ctx.occurredAt !== undefined)
|
|
179
|
+
payload.occurredAt = ctx.occurredAt;
|
|
180
|
+
out.push(payload);
|
|
181
|
+
});
|
|
182
|
+
return out;
|
|
183
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// src/lib/agent-decision/types.ts
|
|
3
|
+
//
|
|
4
|
+
// The provider-neutral canonical contract for agent-human decision capture.
|
|
5
|
+
// This is the ONE source the rest of the pipeline conforms to (spec
|
|
6
|
+
// notes/20260608-agent-decision-capture-design.md, sections 6 and 7).
|
|
7
|
+
//
|
|
8
|
+
// The domain primitive is an "agent-human decision": the agent asked a human to
|
|
9
|
+
// steer, the human answered. Claude Code's AskUserQuestion is the first (and for
|
|
10
|
+
// now only) producer behind the normalize() seam. NOTHING in these types names a
|
|
11
|
+
// Claude tool concept as a canonical field; provider specifics live only in the
|
|
12
|
+
// explicitly provider-scoped fields and in rawProviderPayload (INV-ADAPTER-BOUNDARY).
|
|
13
|
+
//
|
|
14
|
+
// Control mirrors this contract as a class-validator DTO. The CLI cannot import
|
|
15
|
+
// across the nested-workspace boundary into apps/control, so the two definitions
|
|
16
|
+
// are kept in sync by the contract tests on both sides plus the synthetic
|
|
17
|
+
// non-Claude ingest test (INV-CLAUDE-FIRST-NOT-ONLY). The spec is the source of
|
|
18
|
+
// truth for both.
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.AGENT_DECISION_EVENT = void 0;
|
|
21
|
+
exports.AGENT_DECISION_EVENT = "agent_decision_captured";
|