@miadi/plan-insight 0.1.0
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 +21 -0
- package/README.md +91 -0
- package/dist/discovery.d.ts +2 -0
- package/dist/discovery.js +33 -0
- package/dist/errors.d.ts +11 -0
- package/dist/errors.js +16 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +11 -0
- package/dist/orchestrator.d.ts +2 -0
- package/dist/orchestrator.js +167 -0
- package/dist/prepare.d.ts +2 -0
- package/dist/prepare.js +51 -0
- package/dist/types.d.ts +88 -0
- package/dist/types.js +2 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Miadi
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# `@miadi/plan-insight`
|
|
2
|
+
|
|
3
|
+
Capability-injected orchestration for interpreting a plan without pretending that interpretation infrastructure exists.
|
|
4
|
+
|
|
5
|
+
`@miadi/plan-insight` receives a plan plus provenance, requires real perspective, chart, trace, and memory capabilities, runs them in order, and returns deterministic artifact wrappers together with a validated `miadi.miette.perspective.ready` event.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @miadi/plan-insight
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Boundary
|
|
14
|
+
|
|
15
|
+
This package owns:
|
|
16
|
+
|
|
17
|
+
- validating the minimum plan-insight request;
|
|
18
|
+
- deterministic run and artifact IDs;
|
|
19
|
+
- required-capability preflight;
|
|
20
|
+
- ordered perspective → chart → trace → memory orchestration;
|
|
21
|
+
- typed, stage-specific failures;
|
|
22
|
+
- hooks-core perspective discovery and ready-event composition.
|
|
23
|
+
|
|
24
|
+
The injected capabilities own all interpretation and side effects. The package does not generate Mia/Miette prose, create structural-tension charts, contact tracing services, write memory, call an LLM, scan arbitrary host paths, use SSH, or load credentials.
|
|
25
|
+
|
|
26
|
+
The existing Next.js route at `app/api/session/plan-insight` is a **future consumer**. It is not this package's transport or runtime boundary.
|
|
27
|
+
|
|
28
|
+
## Required capabilities
|
|
29
|
+
|
|
30
|
+
```ts
|
|
31
|
+
import { runPlanInsight } from "@miadi/plan-insight"
|
|
32
|
+
|
|
33
|
+
const result = await runPlanInsight(input, {
|
|
34
|
+
perspective: perspectiveProvider,
|
|
35
|
+
chart: chartProvider,
|
|
36
|
+
trace: traceProvider,
|
|
37
|
+
memory: memoryProvider,
|
|
38
|
+
})
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
All four capabilities must be own data properties. Missing capabilities fail before any capability runs. Accessor registries, malformed outputs, provider failures, and hooks-core contract failures produce `PlanInsightError` with an explicit `code` and `stage`.
|
|
42
|
+
|
|
43
|
+
Each capability receives the caller's plan/provenance context and deterministic IDs. Provider references remain available as `externalId`; provider provenance remains attached to its artifact.
|
|
44
|
+
|
|
45
|
+
## hooks-core reuse
|
|
46
|
+
|
|
47
|
+
The package consumes `@miadi/hooks-core@0.4.0` rather than redefining:
|
|
48
|
+
|
|
49
|
+
- `deriveIdempotencyKey`;
|
|
50
|
+
- `discoverPlanPerspectives`;
|
|
51
|
+
- `perspectiveReadyEventFromRecord`;
|
|
52
|
+
- `validatePlanReviewEvent`;
|
|
53
|
+
- `planReviewIdempotencyKey`;
|
|
54
|
+
- plan provenance, source, perspective record, and ready-event types.
|
|
55
|
+
|
|
56
|
+
Use `discoverPlanInsightPerspectives(root, options)` when a consumer needs existing session perspectives lifted into deterministic, validated ready events.
|
|
57
|
+
|
|
58
|
+
## Memory contract
|
|
59
|
+
|
|
60
|
+
The memory capability returns `EpisodicMemory` from `@miadi/episodic-memory-schema@0.2.0`. In this CommonJS release that package is consumed as a **type-only** dependency; no CommonJS runtime import of its ESM entrypoint is emitted.
|
|
61
|
+
|
|
62
|
+
Plan Insight does not consume or duplicate `EpisodeObservation`, and it does not perform Chronicle promotion.
|
|
63
|
+
|
|
64
|
+
## Provenance and determinism
|
|
65
|
+
|
|
66
|
+
The caller supplies timestamps and provenance. IDs derive from hooks-core's stable idempotency function over the plan, episode key, source, and provenance. The package does not call the clock or generate random IDs.
|
|
67
|
+
|
|
68
|
+
Capability artifacts receive deterministic package IDs while real provider/service IDs are preserved separately as `externalId`.
|
|
69
|
+
|
|
70
|
+
Each capability receives an isolated snapshot of the canonical request and prepared IDs. Provider mutation cannot rewrite the provenance or deterministic IDs used for later stages, events, or the final result.
|
|
71
|
+
|
|
72
|
+
## Non-goals for 0.1.0
|
|
73
|
+
|
|
74
|
+
- CLI
|
|
75
|
+
- HTTP routes or job storage
|
|
76
|
+
- provider implementations
|
|
77
|
+
- fake or deterministic substitute interpretations
|
|
78
|
+
- Passages or Forgewright integration
|
|
79
|
+
- episode registration/publication
|
|
80
|
+
- LLM, MCP, Langfuse, Medicine Wheel, or filesystem wiring
|
|
81
|
+
|
|
82
|
+
## Development
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm test
|
|
86
|
+
npm run type-check
|
|
87
|
+
npm run build
|
|
88
|
+
npm pack --dry-run
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
`TDD-EVIDENCE.md` records the red/green sequence used for each 0.1.0 behavior.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.discoverPlanInsightPerspectives = discoverPlanInsightPerspectives;
|
|
4
|
+
const hooks_core_1 = require("@miadi/hooks-core");
|
|
5
|
+
function discoverPlanInsightPerspectives(rootDir, options) {
|
|
6
|
+
return (0, hooks_core_1.discoverPlanPerspectives)(rootDir).map((record) => {
|
|
7
|
+
const seed = (0, hooks_core_1.deriveIdempotencyKey)({
|
|
8
|
+
sessionId: record.session_id,
|
|
9
|
+
agent: options.source.agent ?? options.source.system,
|
|
10
|
+
nativeEvent: "miadi.miette.perspective.ready",
|
|
11
|
+
payload: {
|
|
12
|
+
episode_key: options.episodeKey,
|
|
13
|
+
record,
|
|
14
|
+
source: options.source,
|
|
15
|
+
plan_pane_id: options.planPaneId ?? "unknown",
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
const event = (0, hooks_core_1.perspectiveReadyEventFromRecord)(record, {
|
|
19
|
+
episode_key: options.episodeKey,
|
|
20
|
+
source: { ...options.source },
|
|
21
|
+
plan_pane_id: options.planPaneId,
|
|
22
|
+
event_id: `evt_plan_insight_discovery_${seed.slice(0, 24)}`,
|
|
23
|
+
occurred_at: record.modified_at,
|
|
24
|
+
});
|
|
25
|
+
const validation = (0, hooks_core_1.validatePlanReviewEvent)(event);
|
|
26
|
+
return {
|
|
27
|
+
record,
|
|
28
|
+
event,
|
|
29
|
+
validation,
|
|
30
|
+
idempotencyKey: (0, hooks_core_1.planReviewIdempotencyKey)(event),
|
|
31
|
+
};
|
|
32
|
+
});
|
|
33
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export type PlanInsightErrorCode = "request.invalid" | "capability.missing" | "capability.failed" | "contract.invalid";
|
|
2
|
+
export type PlanInsightStage = "request" | "perspective" | "chart" | "trace" | "memory" | "contract";
|
|
3
|
+
export declare class PlanInsightError extends Error {
|
|
4
|
+
readonly code: PlanInsightErrorCode;
|
|
5
|
+
readonly stage: PlanInsightStage;
|
|
6
|
+
readonly details?: Record<string, unknown>;
|
|
7
|
+
constructor(code: PlanInsightErrorCode, stage: PlanInsightStage, message: string, options?: {
|
|
8
|
+
cause?: unknown;
|
|
9
|
+
details?: Record<string, unknown>;
|
|
10
|
+
});
|
|
11
|
+
}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PlanInsightError = void 0;
|
|
4
|
+
class PlanInsightError extends Error {
|
|
5
|
+
code;
|
|
6
|
+
stage;
|
|
7
|
+
details;
|
|
8
|
+
constructor(code, stage, message, options = {}) {
|
|
9
|
+
super(message, options.cause === undefined ? undefined : { cause: options.cause });
|
|
10
|
+
this.name = "PlanInsightError";
|
|
11
|
+
this.code = code;
|
|
12
|
+
this.stage = stage;
|
|
13
|
+
this.details = options.details;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
exports.PlanInsightError = PlanInsightError;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { PlanInsightError } from "./errors";
|
|
2
|
+
export type { PlanInsightErrorCode, PlanInsightStage, } from "./errors";
|
|
3
|
+
export { discoverPlanInsightPerspectives } from "./discovery";
|
|
4
|
+
export { preparePlanInsight } from "./prepare";
|
|
5
|
+
export { runPlanInsight } from "./orchestrator";
|
|
6
|
+
export type { CapabilityOutput, ChartCapability, DiscoveredPlanInsightPerspective, DiscoverPlanInsightPerspectivesOptions, MemoryCapability, PerspectiveCapability, PerspectiveCapabilityOutput, PlanInsightArtifact, PlanInsightArtifactIds, PlanInsightBaseContext, PlanInsightCapabilities, PlanInsightCapabilityName, PlanInsightInput, PlanInsightProvenance, PlanInsightResult, PreparedPlanInsight, TraceCapability, } from "./types";
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runPlanInsight = exports.preparePlanInsight = exports.discoverPlanInsightPerspectives = exports.PlanInsightError = void 0;
|
|
4
|
+
var errors_1 = require("./errors");
|
|
5
|
+
Object.defineProperty(exports, "PlanInsightError", { enumerable: true, get: function () { return errors_1.PlanInsightError; } });
|
|
6
|
+
var discovery_1 = require("./discovery");
|
|
7
|
+
Object.defineProperty(exports, "discoverPlanInsightPerspectives", { enumerable: true, get: function () { return discovery_1.discoverPlanInsightPerspectives; } });
|
|
8
|
+
var prepare_1 = require("./prepare");
|
|
9
|
+
Object.defineProperty(exports, "preparePlanInsight", { enumerable: true, get: function () { return prepare_1.preparePlanInsight; } });
|
|
10
|
+
var orchestrator_1 = require("./orchestrator");
|
|
11
|
+
Object.defineProperty(exports, "runPlanInsight", { enumerable: true, get: function () { return orchestrator_1.runPlanInsight; } });
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import type { PlanInsightCapabilities, PlanInsightInput, PlanInsightResult } from "./types";
|
|
2
|
+
export declare function runPlanInsight<P = unknown, C = unknown, T = unknown>(input: PlanInsightInput, capabilities: Partial<PlanInsightCapabilities<P, C, T>> | null | undefined): Promise<PlanInsightResult<P, C, T>>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runPlanInsight = runPlanInsight;
|
|
4
|
+
const hooks_core_1 = require("@miadi/hooks-core");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
const prepare_1 = require("./prepare");
|
|
7
|
+
const REQUIRED_CAPABILITIES = [
|
|
8
|
+
"perspective",
|
|
9
|
+
"chart",
|
|
10
|
+
"trace",
|
|
11
|
+
"memory",
|
|
12
|
+
];
|
|
13
|
+
function resolveCapabilities(registry) {
|
|
14
|
+
if (registry === null || typeof registry !== "object") {
|
|
15
|
+
throw new errors_1.PlanInsightError("capability.missing", "request", `Missing required Plan Insight capabilities: ${REQUIRED_CAPABILITIES.join(", ")}`, { details: { missing: [...REQUIRED_CAPABILITIES] } });
|
|
16
|
+
}
|
|
17
|
+
let descriptors;
|
|
18
|
+
try {
|
|
19
|
+
descriptors = Object.getOwnPropertyDescriptors(registry);
|
|
20
|
+
}
|
|
21
|
+
catch (cause) {
|
|
22
|
+
throw new errors_1.PlanInsightError("contract.invalid", "request", "Capability registry descriptors could not be inspected safely", { cause });
|
|
23
|
+
}
|
|
24
|
+
const accessors = REQUIRED_CAPABILITIES.filter((name) => {
|
|
25
|
+
const descriptor = descriptors[name];
|
|
26
|
+
return descriptor !== undefined && !("value" in descriptor);
|
|
27
|
+
});
|
|
28
|
+
if (accessors.length > 0) {
|
|
29
|
+
throw new errors_1.PlanInsightError("contract.invalid", "request", "Capability registry must use own data properties, not accessors", { details: { accessors } });
|
|
30
|
+
}
|
|
31
|
+
const missing = REQUIRED_CAPABILITIES.filter((name) => typeof descriptors[name]?.value !== "function");
|
|
32
|
+
if (missing.length > 0) {
|
|
33
|
+
throw new errors_1.PlanInsightError("capability.missing", "request", `Missing required Plan Insight capabilities: ${missing.join(", ")}`, { details: { missing } });
|
|
34
|
+
}
|
|
35
|
+
return Object.fromEntries(REQUIRED_CAPABILITIES.map((name) => [name, descriptors[name].value]));
|
|
36
|
+
}
|
|
37
|
+
async function executeCapability(stage, execute) {
|
|
38
|
+
try {
|
|
39
|
+
return await execute();
|
|
40
|
+
}
|
|
41
|
+
catch (cause) {
|
|
42
|
+
throw new errors_1.PlanInsightError("capability.failed", stage, `${stage} capability failed`, { cause });
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function normalizeCapabilityOutput(stage, raw) {
|
|
46
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
47
|
+
throw new errors_1.PlanInsightError("contract.invalid", stage, `${stage} capability must return a CapabilityOutput object`);
|
|
48
|
+
}
|
|
49
|
+
let descriptors;
|
|
50
|
+
try {
|
|
51
|
+
descriptors = Object.getOwnPropertyDescriptors(raw);
|
|
52
|
+
}
|
|
53
|
+
catch (cause) {
|
|
54
|
+
throw new errors_1.PlanInsightError("contract.invalid", stage, `${stage} capability output could not be inspected safely`, { cause });
|
|
55
|
+
}
|
|
56
|
+
for (const field of ["value", "externalId", "provenance"]) {
|
|
57
|
+
const descriptor = descriptors[field];
|
|
58
|
+
if (descriptor !== undefined && !("value" in descriptor)) {
|
|
59
|
+
throw new errors_1.PlanInsightError("contract.invalid", stage, `${stage} capability output must use data properties`, { details: { field } });
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (!("value" in (descriptors.value ?? {})) || descriptors.value.value === undefined) {
|
|
63
|
+
throw new errors_1.PlanInsightError("contract.invalid", stage, `${stage} capability output requires a defined value`);
|
|
64
|
+
}
|
|
65
|
+
if (descriptors.externalId?.value !== undefined &&
|
|
66
|
+
typeof descriptors.externalId.value !== "string") {
|
|
67
|
+
throw new errors_1.PlanInsightError("contract.invalid", stage, `${stage} capability externalId must be a string when present`);
|
|
68
|
+
}
|
|
69
|
+
if (descriptors.provenance?.value !== undefined &&
|
|
70
|
+
(typeof descriptors.provenance.value !== "object" ||
|
|
71
|
+
descriptors.provenance.value === null ||
|
|
72
|
+
Array.isArray(descriptors.provenance.value))) {
|
|
73
|
+
throw new errors_1.PlanInsightError("contract.invalid", stage, `${stage} capability provenance must be an object when present`);
|
|
74
|
+
}
|
|
75
|
+
return {
|
|
76
|
+
value: descriptors.value.value,
|
|
77
|
+
externalId: descriptors.externalId?.value,
|
|
78
|
+
provenance: descriptors.provenance?.value,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
function normalizePerspectiveOutput(raw) {
|
|
82
|
+
const output = normalizeCapabilityOutput("perspective", raw);
|
|
83
|
+
const descriptors = Object.getOwnPropertyDescriptors(raw);
|
|
84
|
+
const recordDescriptor = descriptors.record;
|
|
85
|
+
if (recordDescriptor === undefined || !("value" in recordDescriptor)) {
|
|
86
|
+
throw new errors_1.PlanInsightError("contract.invalid", "perspective", "Perspective capability must return a hooks-core PlanPerspectiveRecord");
|
|
87
|
+
}
|
|
88
|
+
const record = recordDescriptor.value;
|
|
89
|
+
if (typeof record !== "object" || record === null || Array.isArray(record)) {
|
|
90
|
+
throw new errors_1.PlanInsightError("contract.invalid", "perspective", "Perspective capability record must be an object");
|
|
91
|
+
}
|
|
92
|
+
return { ...output, record: record };
|
|
93
|
+
}
|
|
94
|
+
function artifact(kind, prepared, output) {
|
|
95
|
+
return {
|
|
96
|
+
id: prepared.artifactIds[kind],
|
|
97
|
+
kind,
|
|
98
|
+
value: output.value,
|
|
99
|
+
externalId: output.externalId,
|
|
100
|
+
provenance: output.provenance,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
function validatePerspectiveOutput(output, input) {
|
|
104
|
+
if (!output || !output.record) {
|
|
105
|
+
throw new errors_1.PlanInsightError("contract.invalid", "perspective", "Perspective capability must return a hooks-core PlanPerspectiveRecord");
|
|
106
|
+
}
|
|
107
|
+
if (output.record.session_id !== input.provenance.source_session_id) {
|
|
108
|
+
throw new errors_1.PlanInsightError("contract.invalid", "perspective", "Perspective record session_id does not match request provenance", {
|
|
109
|
+
details: {
|
|
110
|
+
expected: input.provenance.source_session_id,
|
|
111
|
+
actual: output.record.session_id,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (output.record.latest_plan_path !== input.provenance.plan_path) {
|
|
116
|
+
throw new errors_1.PlanInsightError("contract.invalid", "perspective", "Perspective record latest_plan_path does not match request provenance", {
|
|
117
|
+
details: {
|
|
118
|
+
expected: input.provenance.plan_path,
|
|
119
|
+
actual: output.record.latest_plan_path,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function runPlanInsight(input, capabilities) {
|
|
125
|
+
const prepared = (0, prepare_1.preparePlanInsight)(input);
|
|
126
|
+
const complete = resolveCapabilities(capabilities);
|
|
127
|
+
const canonicalInput = structuredClone(input);
|
|
128
|
+
const baseContext = () => ({
|
|
129
|
+
input: structuredClone(canonicalInput),
|
|
130
|
+
prepared: structuredClone(prepared),
|
|
131
|
+
});
|
|
132
|
+
const rawPerspectiveOutput = await executeCapability("perspective", () => complete.perspective(baseContext()));
|
|
133
|
+
const perspectiveOutput = normalizePerspectiveOutput(rawPerspectiveOutput);
|
|
134
|
+
validatePerspectiveOutput(perspectiveOutput, canonicalInput);
|
|
135
|
+
const perspective = artifact("perspective", prepared, perspectiveOutput);
|
|
136
|
+
const rawChartOutput = await executeCapability("chart", () => complete.chart({ ...baseContext(), perspective }));
|
|
137
|
+
const chartOutput = normalizeCapabilityOutput("chart", rawChartOutput);
|
|
138
|
+
const chart = artifact("chart", prepared, chartOutput);
|
|
139
|
+
const rawTraceOutput = await executeCapability("trace", () => complete.trace({ ...baseContext(), perspective, chart }));
|
|
140
|
+
const traceOutput = normalizeCapabilityOutput("trace", rawTraceOutput);
|
|
141
|
+
const trace = artifact("trace", prepared, traceOutput);
|
|
142
|
+
const rawMemoryOutput = await executeCapability("memory", () => complete.memory({ ...baseContext(), perspective, chart, trace }));
|
|
143
|
+
const memoryOutput = normalizeCapabilityOutput("memory", rawMemoryOutput);
|
|
144
|
+
const memory = artifact("memory", prepared, memoryOutput);
|
|
145
|
+
const perspectiveReadyEvent = (0, hooks_core_1.perspectiveReadyEventFromRecord)(perspectiveOutput.record, {
|
|
146
|
+
episode_key: canonicalInput.episodeKey,
|
|
147
|
+
source: { ...canonicalInput.source },
|
|
148
|
+
plan_pane_id: canonicalInput.provenance.plan_pane_id,
|
|
149
|
+
event_id: `evt_plan_insight_${prepared.idempotencyKey.slice(0, 24)}`,
|
|
150
|
+
occurred_at: canonicalInput.capturedAt,
|
|
151
|
+
});
|
|
152
|
+
const validation = (0, hooks_core_1.validatePlanReviewEvent)(perspectiveReadyEvent);
|
|
153
|
+
if (!validation.valid) {
|
|
154
|
+
throw new errors_1.PlanInsightError("contract.invalid", "contract", "hooks-core rejected the perspective-ready event", { details: { errors: validation.errors } });
|
|
155
|
+
}
|
|
156
|
+
return {
|
|
157
|
+
runId: prepared.runId,
|
|
158
|
+
idempotencyKey: prepared.idempotencyKey,
|
|
159
|
+
provenance: { ...prepared.provenance },
|
|
160
|
+
perspective,
|
|
161
|
+
chart,
|
|
162
|
+
trace,
|
|
163
|
+
memory,
|
|
164
|
+
perspectiveReadyEvent,
|
|
165
|
+
perspectiveReadyIdempotencyKey: (0, hooks_core_1.planReviewIdempotencyKey)(perspectiveReadyEvent),
|
|
166
|
+
};
|
|
167
|
+
}
|
package/dist/prepare.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.preparePlanInsight = preparePlanInsight;
|
|
4
|
+
const hooks_core_1 = require("@miadi/hooks-core");
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
function requireText(value, field) {
|
|
7
|
+
if (typeof value !== "string" || value.trim().length === 0) {
|
|
8
|
+
throw new errors_1.PlanInsightError("request.invalid", "request", `${field} must be a non-empty string`, { details: { field } });
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function copyProvenance(provenance) {
|
|
12
|
+
return { ...provenance };
|
|
13
|
+
}
|
|
14
|
+
function preparePlanInsight(input) {
|
|
15
|
+
requireText(input.episodeKey, "episodeKey");
|
|
16
|
+
requireText(input.planContent, "planContent");
|
|
17
|
+
requireText(input.planFilename, "planFilename");
|
|
18
|
+
requireText(input.source?.system, "source.system");
|
|
19
|
+
requireText(input.provenance?.source_session_id, "provenance.source_session_id");
|
|
20
|
+
requireText(input.provenance?.trace_id, "provenance.trace_id");
|
|
21
|
+
requireText(input.provenance?.plan_pane_id, "provenance.plan_pane_id");
|
|
22
|
+
requireText(input.provenance?.plan_path, "provenance.plan_path");
|
|
23
|
+
if (!input.capturedAt || Number.isNaN(Date.parse(input.capturedAt))) {
|
|
24
|
+
throw new errors_1.PlanInsightError("request.invalid", "request", "capturedAt must be a parseable date-time", { details: { field: "capturedAt" } });
|
|
25
|
+
}
|
|
26
|
+
const provenance = copyProvenance(input.provenance);
|
|
27
|
+
const idempotencyKey = (0, hooks_core_1.deriveIdempotencyKey)({
|
|
28
|
+
sessionId: provenance.source_session_id,
|
|
29
|
+
agent: input.source.agent ?? input.source.system,
|
|
30
|
+
nativeEvent: "plan-insight.run",
|
|
31
|
+
payload: {
|
|
32
|
+
episode_key: input.episodeKey,
|
|
33
|
+
plan_content: input.planContent,
|
|
34
|
+
plan_filename: input.planFilename,
|
|
35
|
+
provenance,
|
|
36
|
+
source: { ...input.source },
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
const runId = `plan-insight:${idempotencyKey}`;
|
|
40
|
+
return {
|
|
41
|
+
runId,
|
|
42
|
+
idempotencyKey,
|
|
43
|
+
artifactIds: {
|
|
44
|
+
perspective: `${runId}:perspective`,
|
|
45
|
+
chart: `${runId}:chart`,
|
|
46
|
+
trace: `${runId}:trace`,
|
|
47
|
+
memory: `${runId}:memory`,
|
|
48
|
+
},
|
|
49
|
+
provenance,
|
|
50
|
+
};
|
|
51
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import type { EpisodicMemory } from "@miadi/episodic-memory-schema";
|
|
2
|
+
import type { PlanPerspectiveRecord, PlanProvenance, PlanReviewEventEnvelope, PlanReviewSource, PlanReviewValidation } from "@miadi/hooks-core";
|
|
3
|
+
export type PlanInsightProvenance = PlanProvenance & {
|
|
4
|
+
source_session_id: string;
|
|
5
|
+
trace_id: string;
|
|
6
|
+
plan_pane_id: string;
|
|
7
|
+
plan_path: string;
|
|
8
|
+
};
|
|
9
|
+
export interface PlanInsightInput {
|
|
10
|
+
episodeKey: string;
|
|
11
|
+
planContent: string;
|
|
12
|
+
planFilename: string;
|
|
13
|
+
source: PlanReviewSource;
|
|
14
|
+
provenance: PlanInsightProvenance;
|
|
15
|
+
/** Caller-supplied timestamp; the package does not invent execution time. */
|
|
16
|
+
capturedAt: string;
|
|
17
|
+
}
|
|
18
|
+
export type PlanInsightArtifactIds = {
|
|
19
|
+
perspective: string;
|
|
20
|
+
chart: string;
|
|
21
|
+
trace: string;
|
|
22
|
+
memory: string;
|
|
23
|
+
};
|
|
24
|
+
export interface PreparedPlanInsight {
|
|
25
|
+
runId: string;
|
|
26
|
+
idempotencyKey: string;
|
|
27
|
+
artifactIds: PlanInsightArtifactIds;
|
|
28
|
+
provenance: PlanInsightProvenance;
|
|
29
|
+
}
|
|
30
|
+
export type PlanInsightCapabilityName = "perspective" | "chart" | "trace" | "memory";
|
|
31
|
+
export interface CapabilityOutput<T> {
|
|
32
|
+
value: T;
|
|
33
|
+
/** Real provider/service reference, when one exists. */
|
|
34
|
+
externalId?: string;
|
|
35
|
+
provenance?: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
export interface PerspectiveCapabilityOutput<T> extends CapabilityOutput<T> {
|
|
38
|
+
record: PlanPerspectiveRecord;
|
|
39
|
+
}
|
|
40
|
+
export interface PlanInsightArtifact<T> extends CapabilityOutput<T> {
|
|
41
|
+
id: string;
|
|
42
|
+
kind: PlanInsightCapabilityName;
|
|
43
|
+
}
|
|
44
|
+
export interface PlanInsightBaseContext {
|
|
45
|
+
input: PlanInsightInput;
|
|
46
|
+
prepared: PreparedPlanInsight;
|
|
47
|
+
}
|
|
48
|
+
export type PerspectiveCapability<T> = (context: PlanInsightBaseContext) => Promise<PerspectiveCapabilityOutput<T>>;
|
|
49
|
+
export type ChartCapability<P, C> = (context: PlanInsightBaseContext & {
|
|
50
|
+
perspective: PlanInsightArtifact<P>;
|
|
51
|
+
}) => Promise<CapabilityOutput<C>>;
|
|
52
|
+
export type TraceCapability<P, C, T> = (context: PlanInsightBaseContext & {
|
|
53
|
+
perspective: PlanInsightArtifact<P>;
|
|
54
|
+
chart: PlanInsightArtifact<C>;
|
|
55
|
+
}) => Promise<CapabilityOutput<T>>;
|
|
56
|
+
export type MemoryCapability<P, C, T> = (context: PlanInsightBaseContext & {
|
|
57
|
+
perspective: PlanInsightArtifact<P>;
|
|
58
|
+
chart: PlanInsightArtifact<C>;
|
|
59
|
+
trace: PlanInsightArtifact<T>;
|
|
60
|
+
}) => Promise<CapabilityOutput<EpisodicMemory>>;
|
|
61
|
+
export interface PlanInsightCapabilities<P = unknown, C = unknown, T = unknown> {
|
|
62
|
+
perspective: PerspectiveCapability<P>;
|
|
63
|
+
chart: ChartCapability<P, C>;
|
|
64
|
+
trace: TraceCapability<P, C, T>;
|
|
65
|
+
memory: MemoryCapability<P, C, T>;
|
|
66
|
+
}
|
|
67
|
+
export interface DiscoverPlanInsightPerspectivesOptions {
|
|
68
|
+
episodeKey: string;
|
|
69
|
+
source: PlanReviewSource;
|
|
70
|
+
planPaneId?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface DiscoveredPlanInsightPerspective {
|
|
73
|
+
record: PlanPerspectiveRecord;
|
|
74
|
+
event: PlanReviewEventEnvelope;
|
|
75
|
+
validation: PlanReviewValidation;
|
|
76
|
+
idempotencyKey: string;
|
|
77
|
+
}
|
|
78
|
+
export interface PlanInsightResult<P = unknown, C = unknown, T = unknown> {
|
|
79
|
+
runId: string;
|
|
80
|
+
idempotencyKey: string;
|
|
81
|
+
provenance: PlanInsightProvenance;
|
|
82
|
+
perspective: PlanInsightArtifact<P>;
|
|
83
|
+
chart: PlanInsightArtifact<C>;
|
|
84
|
+
trace: PlanInsightArtifact<T>;
|
|
85
|
+
memory: PlanInsightArtifact<EpisodicMemory>;
|
|
86
|
+
perspectiveReadyEvent: PlanReviewEventEnvelope;
|
|
87
|
+
perspectiveReadyIdempotencyKey: string;
|
|
88
|
+
}
|
package/dist/types.js
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@miadi/plan-insight",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Capability-injected orchestration for turning a plan into a validated perspective, chart, trace, and episodic-memory result.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "Miadi",
|
|
7
|
+
"homepage": "https://github.com/jgwill/Miadi/tree/main/packages/plan-insight",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/jgwill/Miadi.git",
|
|
11
|
+
"directory": "packages/plan-insight"
|
|
12
|
+
},
|
|
13
|
+
"type": "commonjs",
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"default": "./dist/index.js"
|
|
20
|
+
},
|
|
21
|
+
"./package.json": "./package.json"
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist",
|
|
25
|
+
"README.md",
|
|
26
|
+
"LICENSE"
|
|
27
|
+
],
|
|
28
|
+
"scripts": {
|
|
29
|
+
"build": "tsc -p tsconfig.build.json",
|
|
30
|
+
"type-check": "tsc -p tsconfig.build.json --noEmit",
|
|
31
|
+
"test": "d=$(mktemp -d \"${TMPDIR:-/tmp}/miadi-plan-insight-test.XXXXXX\") && trap 'rm -rf \"$d\"' EXIT && tsc -p tsconfig.test.json --outDir \"$d\" && NODE_PATH=\"$PWD/node_modules\" node --test \"$d\"/test/*.test.js",
|
|
32
|
+
"prepublishOnly": "npm test && npm run build"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"@miadi/episodic-memory-schema": "0.2.0",
|
|
36
|
+
"@miadi/hooks-core": "0.4.0"
|
|
37
|
+
},
|
|
38
|
+
"devDependencies": {
|
|
39
|
+
"@types/node": "^22.0.0",
|
|
40
|
+
"typescript": "^5.4.0"
|
|
41
|
+
},
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=20"
|
|
47
|
+
}
|
|
48
|
+
}
|