@xandreed/core 0.3.0 → 0.8.0-next.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.
Files changed (164) hide show
  1. package/README.md +1 -1
  2. package/dist/conformance.entity.d.ts +15 -0
  3. package/dist/conformance.entity.js +7 -0
  4. package/dist/decision-record.entity.d.ts +31 -0
  5. package/dist/decision-record.entity.functions.d.ts +6 -0
  6. package/dist/decision-record.entity.functions.js +3 -0
  7. package/dist/decision-record.entity.js +24 -0
  8. package/dist/domain/agent-failure.entity.d.ts +6 -6
  9. package/dist/domain/agent-failure.entity.js +12 -1
  10. package/dist/domain/failure.entity.d.ts +2 -2
  11. package/dist/domain/loop-event.entity.d.ts +2 -0
  12. package/dist/domain/message.entity.d.ts +143 -156
  13. package/dist/domain/message.entity.js +6 -6
  14. package/dist/domain/model-call-policy.entity.d.ts +3 -3
  15. package/dist/domain/model-call-policy.entity.js +1 -1
  16. package/dist/domain/model-catalog.entity.d.ts +4 -4
  17. package/dist/domain/model-catalog.entity.js +1 -1
  18. package/dist/domain/model-selection.entity.d.ts +6 -13
  19. package/dist/domain/prompt-provenance.entity.d.ts +6 -6
  20. package/dist/domain/prompt-provenance.entity.js +4 -4
  21. package/dist/domain/token-usage.entity.d.ts +4 -4
  22. package/dist/harness/capability.entity.d.ts +88 -41
  23. package/dist/harness/capability.entity.functions.d.ts +30 -1
  24. package/dist/harness/capability.entity.functions.js +55 -2
  25. package/dist/harness/capability.entity.js +33 -12
  26. package/dist/harness/config.entity.d.ts +28 -28
  27. package/dist/harness/config.entity.js +4 -4
  28. package/dist/harness/plugin-stack.adapter.d.ts +24 -0
  29. package/dist/harness/plugin-stack.adapter.js +8 -0
  30. package/dist/harness/plugin-stack.adapter.test.d.ts +1 -0
  31. package/dist/harness/plugin.adapter.d.ts +15 -4
  32. package/dist/harness/plugin.adapter.js +18 -11
  33. package/dist/harness/plugin.adapter.test.d.ts +1 -0
  34. package/dist/harness/plugin.entity.d.ts +26 -10
  35. package/dist/harness/plugin.entity.js +1 -1
  36. package/dist/harness/session.entity.d.ts +20 -20
  37. package/dist/harness/session.entity.js +1 -1
  38. package/dist/harness/skill-files.d.ts +17 -0
  39. package/dist/harness/skill-files.js +25 -0
  40. package/dist/index.d.ts +48 -4
  41. package/dist/index.js +46 -4
  42. package/dist/loop/cacheKey.d.ts +3 -3
  43. package/dist/loop/cacheKey.js +4 -2
  44. package/dist/loop/mapping.d.ts +4 -5
  45. package/dist/loop/mapping.js +8 -16
  46. package/dist/loop/modelPolicy.d.ts +4 -4
  47. package/dist/loop/modelPolicy.js +2 -2
  48. package/dist/loop/promptProvenance.d.ts +2 -2
  49. package/dist/loop/promptProvenance.js +2 -2
  50. package/dist/loop/stepContext.d.ts +2 -2
  51. package/dist/loop/stepContext.js +2 -2
  52. package/dist/loop/streamFold.js +3 -3
  53. package/dist/loop/toolSchema.d.ts +17 -0
  54. package/dist/loop/toolSchema.js +25 -0
  55. package/dist/loop/toolSchema.test.d.ts +1 -0
  56. package/dist/mcp/bridge.d.ts +15 -15
  57. package/dist/mcp/bridge.js +14 -14
  58. package/dist/memory/memory-log.entity.d.ts +320 -0
  59. package/dist/memory/memory-log.entity.functions.d.ts +89 -0
  60. package/dist/memory/memory-log.entity.functions.js +180 -0
  61. package/dist/memory/memory-log.entity.js +75 -0
  62. package/dist/memory/memory-log.entity.test.d.ts +1 -0
  63. package/dist/memory/memory-session.d.ts +51 -0
  64. package/dist/memory/memory-session.js +97 -0
  65. package/dist/memory/memory-session.test.d.ts +1 -0
  66. package/dist/memory/memory.conformance.d.ts +32 -0
  67. package/dist/memory/memory.conformance.js +102 -0
  68. package/dist/ports/auth-store.port.d.ts +16 -18
  69. package/dist/ports/auth-store.port.js +13 -9
  70. package/dist/ports/capability.port.d.ts +131 -7
  71. package/dist/ports/capability.port.js +2 -2
  72. package/dist/ports/conversation-store.port.d.ts +54 -177
  73. package/dist/ports/conversation-store.port.js +9 -9
  74. package/dist/ports/file-system.port.d.ts +5 -7
  75. package/dist/ports/file-system.port.js +1 -1
  76. package/dist/ports/harness.port.d.ts +19 -13
  77. package/dist/ports/harness.port.js +10 -10
  78. package/dist/ports/mcp-client.port.d.ts +15 -47
  79. package/dist/ports/mcp-client.port.js +3 -3
  80. package/dist/ports/memory.port.d.ts +116 -0
  81. package/dist/ports/memory.port.js +7 -0
  82. package/dist/ports/model-catalog.port.d.ts +1 -1
  83. package/dist/ports/model-catalog.port.js +1 -1
  84. package/dist/ports/permission.port.d.ts +10 -0
  85. package/dist/ports/permission.port.js +4 -0
  86. package/dist/ports/run-context.port.d.ts +28 -0
  87. package/dist/ports/run-context.port.js +4 -0
  88. package/dist/ports/session-log.port.d.ts +25 -0
  89. package/dist/ports/session-log.port.js +4 -0
  90. package/dist/ports/sessions.port.d.ts +141 -0
  91. package/dist/ports/sessions.port.js +4 -0
  92. package/dist/ports/settings-store.port.d.ts +18 -56
  93. package/dist/ports/settings-store.port.js +3 -3
  94. package/dist/ports/shell.port.d.ts +9 -21
  95. package/dist/ports/shell.port.js +1 -1
  96. package/dist/ports/step-loop.port.d.ts +85 -0
  97. package/dist/ports/step-loop.port.js +4 -0
  98. package/dist/ports/tasks.port.d.ts +59 -0
  99. package/dist/ports/tasks.port.js +10 -0
  100. package/dist/ports/tool-registry.port.d.ts +53 -0
  101. package/dist/ports/tool-registry.port.js +4 -0
  102. package/dist/ports/turn-admission.port.d.ts +23 -0
  103. package/dist/ports/turn-admission.port.js +4 -0
  104. package/dist/ports/turn-events.port.d.ts +55 -0
  105. package/dist/ports/turn-events.port.js +7 -0
  106. package/dist/ports/turn-scope.port.d.ts +101 -0
  107. package/dist/ports/turn-scope.port.js +10 -0
  108. package/dist/ports/turn.port.d.ts +113 -0
  109. package/dist/ports/turn.port.js +0 -0
  110. package/dist/ports/utility-llm.port.d.ts +12 -31
  111. package/dist/ports/utility-llm.port.js +1 -1
  112. package/dist/session/chassis.js +7 -7
  113. package/dist/session/session-event.entity.d.ts +55 -0
  114. package/dist/session/session-event.entity.functions.d.ts +31 -0
  115. package/dist/session/session-event.entity.functions.js +42 -0
  116. package/dist/session/session-event.entity.js +37 -0
  117. package/dist/session/session-event.entity.test.d.ts +1 -0
  118. package/dist/session/session-log.conformance.d.ts +13 -0
  119. package/dist/session/session-log.conformance.js +167 -0
  120. package/dist/session/session-log.entity.d.ts +164 -0
  121. package/dist/session/session-log.entity.js +82 -0
  122. package/dist/session/session-log.memory.adapter.d.ts +7 -0
  123. package/dist/session/session-log.memory.adapter.js +82 -0
  124. package/dist/session/session-log.memory.adapter.test.d.ts +1 -0
  125. package/dist/session/sessions.entity.d.ts +201 -0
  126. package/dist/session/sessions.entity.js +98 -0
  127. package/dist/session/tasks.entity.d.ts +39 -0
  128. package/dist/session/tasks.entity.js +26 -0
  129. package/dist/session/turn-admission.open.adapter.d.ts +4 -0
  130. package/dist/session/turn-admission.open.adapter.js +3 -0
  131. package/dist/spec/SpecDoc.d.ts +30 -117
  132. package/dist/spec/SpecDoc.js +10 -10
  133. package/dist/spec/codec.js +1 -1
  134. package/dist/turn/model-request.entity.d.ts +99 -0
  135. package/dist/turn/model-request.entity.functions.d.ts +108 -0
  136. package/dist/turn/model-request.entity.functions.js +167 -0
  137. package/dist/turn/model-request.entity.js +41 -0
  138. package/dist/turn/model-request.entity.test.d.ts +1 -0
  139. package/dist/turn/prompt-sections.d.ts +10 -0
  140. package/dist/turn/prompt-sections.js +3 -0
  141. package/dist/turn/step-loop.conformance.d.ts +16 -0
  142. package/dist/turn/step-loop.conformance.js +164 -0
  143. package/dist/turn/testing.d.ts +30 -0
  144. package/dist/turn/turn-bus.d.ts +23 -0
  145. package/dist/turn/turn-bus.js +54 -0
  146. package/dist/turn/turn-bus.test.d.ts +1 -0
  147. package/dist/turn/turn-event.entity.d.ts +263 -0
  148. package/dist/turn/turn-event.entity.functions.d.ts +35 -0
  149. package/dist/turn/turn-event.entity.functions.js +21 -0
  150. package/dist/turn/turn-event.entity.js +101 -0
  151. package/dist/turn/turn-lifecycle.d.ts +32 -0
  152. package/dist/turn/turn-lifecycle.js +48 -0
  153. package/dist/turn/turn-run.d.ts +26 -0
  154. package/dist/turn/turn-run.js +105 -0
  155. package/dist/turn/turn.adapter.d.ts +21 -0
  156. package/dist/turn/turn.adapter.js +126 -0
  157. package/dist/turn/turn.adapter.test.d.ts +1 -0
  158. package/dist/turn/turn.conformance.d.ts +20 -0
  159. package/dist/turn/turn.conformance.js +74 -0
  160. package/dist/turn/user-message.entity.d.ts +14 -0
  161. package/dist/turn/user-message.entity.js +10 -0
  162. package/dist/util/json.d.ts +2 -2
  163. package/dist/util/json.js +10 -10
  164. package/package.json +2 -3
@@ -1,13 +1,13 @@
1
1
  import { Schema } from "effect";
2
- import type { Context, Effect, Scope } from "effect";
3
- export declare const PLUGIN_API_VERSION = 1;
4
- declare const HarnessError_base: Schema.TaggedErrorClass<HarnessError, "HarnessError", {
5
- readonly _tag: Schema.tag<"HarnessError">;
6
- } & {
7
- code: typeof Schema.String;
8
- message: typeof Schema.String;
9
- plugin: Schema.optional<typeof Schema.String>;
10
- }>;
2
+ import type { Context, Effect, Layer, Scope } from "effect";
3
+ export declare const PLUGIN_API_VERSION = 2;
4
+ /** Version 1 plugins (no capabilities, no optional services) still load. */
5
+ export declare const SUPPORTED_PLUGIN_API_VERSIONS: ReadonlyArray<number>;
6
+ declare const HarnessError_base: Schema.Class<HarnessError, Schema.TaggedStruct<"HarnessError", {
7
+ readonly code: Schema.String;
8
+ readonly message: Schema.String;
9
+ readonly plugin: Schema.optional<Schema.String>;
10
+ }>, import("effect/Cause").YieldableError>;
11
11
  export declare class HarnessError extends HarnessError_base {
12
12
  }
13
13
  /** The runtime consumes this erased boundary; authors use definePlugin. */
@@ -18,8 +18,24 @@ export interface Plugin {
18
18
  readonly scope: "runtime" | "session";
19
19
  readonly requires: ReadonlyArray<string>;
20
20
  readonly provides: ReadonlyArray<string>;
21
- readonly schema: Schema.Schema.AnyNoContext;
21
+ /** Multi-provider keys: every contributor's array is concatenated in graph order. */
22
+ readonly contributes: ReadonlyArray<string>;
23
+ /** Used when present (ordering and dependencies), never required. */
24
+ readonly optional: ReadonlyArray<string>;
25
+ readonly schema: Schema.Codec<any, any>;
22
26
  readonly defaults: Readonly<Record<string, unknown>>;
23
27
  readonly build: (options: unknown, services: Context.Context<never>) => Effect.Effect<Context.Context<never>, HarnessError, Scope.Scope>;
24
28
  }
29
+ /**
30
+ * A plugin as its author defined it (see `definePlugin`): the erased
31
+ * `Plugin` the runtime loads, plus its typed parts. `live` is the plugin's
32
+ * own layer, with the services it provides and requires in its type, for
33
+ * hosts that compose layers themselves (see `stackPlugins`).
34
+ */
35
+ export interface TypedPlugin<A extends Readonly<Record<string, unknown>>, I, Out, E, In> extends Plugin {
36
+ readonly config: Schema.Codec<A, I>;
37
+ readonly defaults: A;
38
+ /** Options merged over the defaults and decoded (extra keys fail with config.options). */
39
+ readonly live: (options?: Partial<I>) => Layer.Layer<Out, E | HarnessError, In>;
40
+ }
25
41
  export {};
@@ -1,5 +1,5 @@
1
1
  import { Schema } from "effect";
2
- export const PLUGIN_API_VERSION = 1;
2
+ export const PLUGIN_API_VERSION = 2, SUPPORTED_PLUGIN_API_VERSIONS = [1, 2];
3
3
 
4
4
  export class HarnessError extends Schema.TaggedError()("HarnessError", {
5
5
  code: Schema.String,
@@ -1,33 +1,33 @@
1
1
  import { Schema } from "effect";
2
2
  export declare const SessionRecord: Schema.Struct<{
3
- id: Schema.brand<typeof Schema.UUID, "ConversationId">;
4
- workspace: typeof Schema.String;
5
- profile: typeof Schema.String;
6
- createdAt: typeof Schema.Number;
7
- parent: Schema.optional<Schema.brand<typeof Schema.UUID, "ConversationId">>;
3
+ readonly id: Schema.brand<Schema.String, "ConversationId">;
4
+ readonly workspace: Schema.String;
5
+ readonly profile: Schema.String;
6
+ readonly createdAt: Schema.Number;
7
+ readonly parent: Schema.optional<Schema.brand<Schema.String, "ConversationId">>;
8
8
  }>;
9
9
  export type SessionRecord = typeof SessionRecord.Type;
10
10
  export declare const EventBody: Schema.Struct<{
11
- name: typeof Schema.NonEmptyString;
12
- runId: Schema.optional<typeof Schema.String>;
13
- data: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
11
+ readonly name: Schema.NonEmptyString;
12
+ readonly runId: Schema.optional<Schema.String>;
13
+ readonly data: Schema.$Record<Schema.String, Schema.Unknown>;
14
14
  }>;
15
15
  export type EventBody = typeof EventBody.Type;
16
16
  export declare const SessionEvent: Schema.Struct<{
17
- version: Schema.Literal<[1]>;
18
- id: typeof Schema.String;
19
- sessionId: Schema.brand<typeof Schema.UUID, "ConversationId">;
20
- seq: typeof Schema.Int;
21
- at: typeof Schema.Number;
22
- name: typeof Schema.NonEmptyString;
23
- runId: Schema.optional<typeof Schema.String>;
24
- data: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
17
+ readonly version: Schema.Literal<1>;
18
+ readonly id: Schema.String;
19
+ readonly sessionId: Schema.brand<Schema.String, "ConversationId">;
20
+ readonly seq: Schema.Int;
21
+ readonly at: Schema.Number;
22
+ readonly name: Schema.NonEmptyString;
23
+ readonly runId: Schema.optional<Schema.String>;
24
+ readonly data: Schema.$Record<Schema.String, Schema.Unknown>;
25
25
  }>;
26
26
  export type SessionEvent = typeof SessionEvent.Type;
27
27
  export declare const MemoryEntry: Schema.Struct<{
28
- id: typeof Schema.String;
29
- workspace: typeof Schema.String;
30
- text: typeof Schema.String;
31
- createdAt: typeof Schema.Number;
28
+ readonly id: Schema.String;
29
+ readonly workspace: Schema.String;
30
+ readonly text: Schema.String;
31
+ readonly createdAt: Schema.Number;
32
32
  }>;
33
33
  export type MemoryEntry = typeof MemoryEntry.Type;
@@ -9,7 +9,7 @@ export const SessionRecord = Schema.Struct({
9
9
  }), EventBody = Schema.Struct({
10
10
  name: Schema.NonEmptyString,
11
11
  runId: Schema.optional(Schema.String),
12
- data: Schema.Record({ key: Schema.String, value: Schema.Unknown })
12
+ data: Schema.Record(Schema.String, Schema.Unknown)
13
13
  }), SessionEvent = Schema.Struct({
14
14
  ...EventBody.fields,
15
15
  version: Schema.Literal(1),
@@ -0,0 +1,17 @@
1
+ import { Effect } from "effect";
2
+ import { SkillDefinition } from "./capability.entity.js";
3
+ import { HarnessError } from "./plugin.entity.js";
4
+ /** One file of a skills tree, as the host read it. */
5
+ export interface SkillFile {
6
+ /** Relative path, `/`-separated: `<skill>/SKILL.md` or `<skill>/references/<id>.md`. */
7
+ readonly path: string;
8
+ readonly text: string;
9
+ }
10
+ /**
11
+ * Skills from markdown files. `<skill>/SKILL.md` carries flat frontmatter —
12
+ * `summary` (required), `id` (defaults to the directory), `version`,
13
+ * `tools` and `permissions` (comma-separated), `always` (`true`) — and its
14
+ * body is the load_skill instructions. Each `<skill>/references/<id>.md` is
15
+ * one reference, titled by its `title` field or first heading.
16
+ */
17
+ export declare const skillsFromFiles: (files: ReadonlyArray<SkillFile>) => Effect.Effect<ReadonlyArray<SkillDefinition>, HarnessError>;
@@ -0,0 +1,25 @@
1
+ import { Effect, Option, Schema } from "effect";
2
+ import { parseFrontmatter } from "../spec/frontmatter.js";
3
+ import { SkillDefinition } from "./capability.entity.js";
4
+ import { HarnessError } from "./plugin.entity.js";
5
+ const invalid = (path, message) => new HarnessError({ code: "skills.files", message: `${path}: ${message}` }), list = (value) => (value ?? "").split(",").map((item) => item.trim()).filter((item) => item.length > 0), headingOf = (body) => Option.fromNullishOr(body.split(`
6
+ `).find((line) => line.startsWith("# "))).pipe(Option.map((line) => line.slice(2).trim()));
7
+ export const skillsFromFiles = (files) => Effect.forEach(files.filter((file) => /^[^/]+\/SKILL\.md$/.test(file.path)), (file) => Effect.gen(function* () {
8
+ const directory = file.path.split("/")[0] ?? "", parsed = yield* Option.match(parseFrontmatter(file.text), {
9
+ onNone: () => Effect.fail(invalid(file.path, "missing frontmatter")),
10
+ onSome: Effect.succeed
11
+ }), references = yield* Effect.forEach(files.filter((candidate) => candidate.path.startsWith(`${directory}/references/`) && candidate.path.endsWith(".md")), (reference) => {
12
+ const id = reference.path.slice(`${directory}/references/`.length, -3), document = Option.getOrElse(parseFrontmatter(reference.text), () => ({ fields: {}, body: reference.text })), title = Option.getOrElse(Option.orElse(Option.fromNullishOr(document.fields.title), () => headingOf(document.body)), () => id);
13
+ return Effect.succeed({ id, title, text: document.body.trim() });
14
+ }), fields = parsed.fields;
15
+ return yield* Schema.decodeUnknownEffect(SkillDefinition)({
16
+ id: fields.id ?? directory,
17
+ version: fields.version ?? "1",
18
+ summary: fields.summary ?? "",
19
+ instructions: parsed.body.trim(),
20
+ tools: list(fields.tools),
21
+ always: fields.always === "true",
22
+ permissions: list(fields.permissions),
23
+ references
24
+ }).pipe(Effect.mapError((error) => invalid(file.path, error.message)));
25
+ }));
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { AgentMessage, AgentResult, AssistantMessage, Checkpoint, ConversationId, ReasoningPart, TextPart, ToolCallId, ToolCallPart, ToolMessage, ToolResultPart, UserMessage, } from "./domain/message.entity.js";
1
+ export { AgentMessage, AgentResult, AssistantMessage, Checkpoint, ConversationId, ReasoningPart, TextPart, ToolCallId, ToolCallPart, ToolMessage, ToolResultPart, UserRoleMessage, } from "./domain/message.entity.js";
2
2
  export { Failure } from "./domain/failure.entity.js";
3
3
  export { toFailure } from "./domain/failure.entity.functions.js";
4
4
  export { AgentFailure, AgentFailureCategory } from "./domain/agent-failure.entity.js";
@@ -28,24 +28,68 @@ export { assistantModel, assistantUsage, extractUsage, extractModel, handoffToMe
28
28
  export type { ToolResultSummary } from "./loop/mapping.js";
29
29
  export { CurrentPromptCacheKey } from "./loop/cacheKey.js";
30
30
  export { foldStreamParts } from "./loop/streamFold.js";
31
+ export { strictJsonSchema, toolParametersSchema } from "./loop/toolSchema.js";
31
32
  export type { FoldedTurn, StreamDelta } from "./loop/streamFold.js";
32
33
  export { McpCallOutcome, McpClient, McpError, McpToolDescriptor } from "./ports/mcp-client.port.js";
33
34
  export { buildMcpBridge, emptyMcpBridge, McpCall, McpDescribe } from "./mcp/bridge.js";
34
35
  export type { McpBridge, McpBridgedTool } from "./mcp/bridge.js";
35
36
  export { makeSession } from "./session/chassis.js";
36
37
  export type { SeqEvent, Session } from "./session/chassis.js";
38
+ export * from "./session/session-log.entity.js";
39
+ export * from "./ports/session-log.port.js";
40
+ export { SessionLogMemoryLive } from "./session/session-log.memory.adapter.js";
41
+ export { sessionLogConformance } from "./session/session-log.conformance.js";
42
+ export * from "./session/session-event.entity.js";
43
+ export * from "./session/session-event.entity.functions.js";
44
+ export * from "./session/sessions.entity.js";
45
+ export * from "./ports/sessions.port.js";
46
+ export * from "./ports/turn-admission.port.js";
47
+ export { TurnAdmissionOpen } from "./session/turn-admission.open.adapter.js";
48
+ export * from "./session/tasks.entity.js";
49
+ export * from "./ports/tasks.port.js";
37
50
  export { DEFAULT_SPEC_LIMITS, renderSpecSection, SpecCheck, SpecDoc, SpecGates, SpecLimits, SpecSlug, SpecStatus, } from "./spec/SpecDoc.js";
38
51
  export { decodeSpecDocText, encodeSpecDocText, SpecDocParseError, specSlug, uniqueSlug, } from "./spec/codec.js";
39
52
  export { parseFrontmatter } from "./spec/frontmatter.js";
40
53
  export * from "./harness/plugin.entity.js";
41
54
  export * from "./harness/plugin.adapter.js";
55
+ export * from "./harness/plugin-stack.adapter.js";
42
56
  export * from "./harness/config.entity.js";
43
57
  export * from "./harness/session.entity.js";
44
58
  export * from "./ports/harness.port.js";
45
59
  export { defineAgent, defineConfig } from "./harness/config.entity.functions.js";
46
- export * from "./harness/capability.entity.js";
47
- export * from "./harness/capability.entity.functions.js";
48
- export * from "./ports/capability.port.js";
49
60
  export * from "./domain/prompt-provenance.entity.js";
50
61
  export * from "./loop/promptProvenance.js";
51
62
  export { CurrentAgentStep } from "./loop/stepContext.js";
63
+ export * from "./decision-record.entity.js";
64
+ export * from "./memory/memory-log.entity.js";
65
+ export * from "./memory/memory-log.entity.functions.js";
66
+ export * from "./memory/memory-session.js";
67
+ export * from "./ports/memory.port.js";
68
+ export * from "./harness/capability.entity.js";
69
+ export * from "./harness/capability.entity.functions.js";
70
+ export * from "./ports/capability.port.js";
71
+ export * from "./ports/permission.port.js";
72
+ export * from "./ports/run-context.port.js";
73
+ export * from "./ports/tool-registry.port.js";
74
+ export * from "./turn/user-message.entity.js";
75
+ export * from "./turn/turn-event.entity.js";
76
+ export * from "./turn/turn-event.entity.functions.js";
77
+ export * from "./turn/model-request.entity.js";
78
+ export * from "./turn/model-request.entity.functions.js";
79
+ export * from "./turn/turn-bus.js";
80
+ export * from "./ports/turn-events.port.js";
81
+ export * from "./ports/step-loop.port.js";
82
+ export * from "./ports/turn.port.js";
83
+ export * from "./ports/turn-scope.port.js";
84
+ export * from "./turn/prompt-sections.js";
85
+ export * from "./turn/turn.adapter.js";
86
+ export * from "./turn/turn-run.js";
87
+ export * from "./turn/turn-lifecycle.js";
88
+ export * from "./decision-record.entity.functions.js";
89
+ export { skillsFromFiles } from "./harness/skill-files.js";
90
+ export type { SkillFile } from "./harness/skill-files.js";
91
+ export { ConformanceFailure } from "./conformance.entity.js";
92
+ export type { ConformanceCheck } from "./conformance.entity.js";
93
+ export { probeCall, stepLoopConformance, textReply } from "./turn/step-loop.conformance.js";
94
+ export { inMemoryLog, memoryConformance } from "./memory/memory.conformance.js";
95
+ export { turnConformance } from "./turn/turn.conformance.js";
package/dist/index.js CHANGED
@@ -10,7 +10,7 @@ export {
10
10
  ToolCallPart,
11
11
  ToolMessage,
12
12
  ToolResultPart,
13
- UserMessage
13
+ UserRoleMessage
14
14
  } from "./domain/message.entity.js";
15
15
  export { Failure } from "./domain/failure.entity.js";
16
16
  export { toFailure } from "./domain/failure.entity.functions.js";
@@ -64,9 +64,22 @@ export {
64
64
  } from "./loop/mapping.js";
65
65
  export { CurrentPromptCacheKey } from "./loop/cacheKey.js";
66
66
  export { foldStreamParts } from "./loop/streamFold.js";
67
+ export { strictJsonSchema, toolParametersSchema } from "./loop/toolSchema.js";
67
68
  export { McpCallOutcome, McpClient, McpError, McpToolDescriptor } from "./ports/mcp-client.port.js";
68
69
  export { buildMcpBridge, emptyMcpBridge, McpCall, McpDescribe } from "./mcp/bridge.js";
69
70
  export { makeSession } from "./session/chassis.js";
71
+ export * from "./session/session-log.entity.js";
72
+ export * from "./ports/session-log.port.js";
73
+ export { SessionLogMemoryLive } from "./session/session-log.memory.adapter.js";
74
+ export { sessionLogConformance } from "./session/session-log.conformance.js";
75
+ export * from "./session/session-event.entity.js";
76
+ export * from "./session/session-event.entity.functions.js";
77
+ export * from "./session/sessions.entity.js";
78
+ export * from "./ports/sessions.port.js";
79
+ export * from "./ports/turn-admission.port.js";
80
+ export { TurnAdmissionOpen } from "./session/turn-admission.open.adapter.js";
81
+ export * from "./session/tasks.entity.js";
82
+ export * from "./ports/tasks.port.js";
70
83
  export {
71
84
  DEFAULT_SPEC_LIMITS,
72
85
  renderSpecSection,
@@ -87,13 +100,42 @@ export {
87
100
  export { parseFrontmatter } from "./spec/frontmatter.js";
88
101
  export * from "./harness/plugin.entity.js";
89
102
  export * from "./harness/plugin.adapter.js";
103
+ export * from "./harness/plugin-stack.adapter.js";
90
104
  export * from "./harness/config.entity.js";
91
105
  export * from "./harness/session.entity.js";
92
106
  export * from "./ports/harness.port.js";
93
107
  export { defineAgent, defineConfig } from "./harness/config.entity.functions.js";
94
- export * from "./harness/capability.entity.js";
95
- export * from "./harness/capability.entity.functions.js";
96
- export * from "./ports/capability.port.js";
97
108
  export * from "./domain/prompt-provenance.entity.js";
98
109
  export * from "./loop/promptProvenance.js";
99
110
  export { CurrentAgentStep } from "./loop/stepContext.js";
111
+ export * from "./decision-record.entity.js";
112
+ export * from "./memory/memory-log.entity.js";
113
+ export * from "./memory/memory-log.entity.functions.js";
114
+ export * from "./memory/memory-session.js";
115
+ export * from "./ports/memory.port.js";
116
+ export * from "./harness/capability.entity.js";
117
+ export * from "./harness/capability.entity.functions.js";
118
+ export * from "./ports/capability.port.js";
119
+ export * from "./ports/permission.port.js";
120
+ export * from "./ports/run-context.port.js";
121
+ export * from "./ports/tool-registry.port.js";
122
+ export * from "./turn/user-message.entity.js";
123
+ export * from "./turn/turn-event.entity.js";
124
+ export * from "./turn/turn-event.entity.functions.js";
125
+ export * from "./turn/model-request.entity.js";
126
+ export * from "./turn/model-request.entity.functions.js";
127
+ export * from "./turn/turn-bus.js";
128
+ export * from "./ports/turn-events.port.js";
129
+ export * from "./ports/step-loop.port.js";
130
+ export * from "./ports/turn.port.js";
131
+ export * from "./ports/turn-scope.port.js";
132
+ export * from "./turn/prompt-sections.js";
133
+ export * from "./turn/turn.adapter.js";
134
+ export * from "./turn/turn-run.js";
135
+ export * from "./turn/turn-lifecycle.js";
136
+ export * from "./decision-record.entity.functions.js";
137
+ export { skillsFromFiles } from "./harness/skill-files.js";
138
+ export { ConformanceFailure } from "./conformance.entity.js";
139
+ export { probeCall, stepLoopConformance, textReply } from "./turn/step-loop.conformance.js";
140
+ export { inMemoryLog, memoryConformance } from "./memory/memory.conformance.js";
141
+ export { turnConformance } from "./turn/turn.conformance.js";
@@ -1,4 +1,4 @@
1
- import { FiberRef, Option } from "effect";
1
+ import { Context, Option } from "effect";
2
2
  /**
3
3
  * The CURRENT conversation's cache identity, stamped by `runAgent` for the
4
4
  * duration of a run. Adapters that can key server-side prompt caches on a
@@ -6,8 +6,8 @@ import { FiberRef, Option } from "effect";
6
6
  * one conversation, one cache lane, so parallel sessions stop evicting
7
7
  * each other's prefixes. `None` (the default) sends nothing.
8
8
  *
9
- * A FiberRef, not a port: it's ambient call metadata, not a capability —
9
+ * A context reference, not a port: it's ambient call metadata, not a capability —
10
10
  * and it must flow through the loop's fibers without threading a parameter
11
11
  * through every seam.
12
12
  */
13
- export declare const CurrentPromptCacheKey: FiberRef.FiberRef<Option.Option<string>>;
13
+ export declare const CurrentPromptCacheKey: Context.Reference<Option.Option<string>>;
@@ -1,2 +1,4 @@
1
- import { FiberRef, GlobalValue, Option } from "effect";
2
- export const CurrentPromptCacheKey = GlobalValue.globalValue("@xandreed/core/CurrentPromptCacheKey", () => FiberRef.unsafeMake(Option.none()));
1
+ import { Context, Option } from "effect";
2
+ export const CurrentPromptCacheKey = Context.Reference("@xandreed/core/CurrentPromptCacheKey", {
3
+ defaultValue: () => Option.none()
4
+ });
@@ -57,11 +57,10 @@ export interface ToolResultSummary {
57
57
  /** Tool-result summaries; `id` matches the originating call's id. */
58
58
  export declare const responseToolResults: (content: ReadonlyArray<unknown>) => ReadonlyArray<ToolResultSummary>;
59
59
  /**
60
- * Token usage from a response's `usage` + the finish part's metadata. The
61
- * Anthropic fold matters: its `input_tokens` EXCLUDES cache reads/writes
62
- * (both ride only in the raw usage on the finish part's anthropic metadata),
63
- * so without folding them back a fully-cached turn reads ~0 input.
64
- * Gemini/OpenAI already include cached tokens in their prompt counts.
60
+ * Token usage from a response's `usage` + the finish part's. Effect's
61
+ * providers report the FULL input (cache reads and writes included, e.g.
62
+ * Anthropic's `input_tokens` plus both cache counts) with the cache reads
63
+ * beside it; Gemini's raw usage metadata is the last fallback.
65
64
  */
66
65
  export declare const extractUsage: (usage: unknown, content: ReadonlyArray<unknown>) => TokenUsage;
67
66
  /**
@@ -98,7 +98,7 @@ export const toPromptMessages = (messages) => messages.map((m) => {
98
98
  type: "tool-result",
99
99
  toolCallId: ToolCallId.make(p.id ?? ""),
100
100
  toolName: p.name ?? "",
101
- output: p.result,
101
+ output: encodedOf(p),
102
102
  isError: p.isFailure ?? !1,
103
103
  ...withProviderOptions(p.metadata)
104
104
  }
@@ -107,25 +107,17 @@ export const toPromptMessages = (messages) => messages.map((m) => {
107
107
  ...assistantParts.length > 0 ? [{ role: "assistant", content: assistantParts }] : [],
108
108
  ...toolParts.length > 0 ? [{ role: "tool", content: toolParts }] : []
109
109
  ];
110
- }, responseText = (content) => content.filter((p) => p.type === "text").map((p) => p.text ?? "").join(""), responseReasoning = (content) => content.filter((p) => p.type === "reasoning").map((p) => p.text ?? "").join("").trim(), responseToolCalls = (content) => content.filter((p) => p.type === "tool-call").map((p) => ({ id: p.id ?? "", toolName: p.name ?? "", args: p.params ?? {} })), responseToolResults = (content) => content.filter((p) => p.type === "tool-result").map((p) => ({
110
+ }, responseText = (content) => content.filter((p) => p.type === "text").map((p) => p.text ?? "").join(""), responseReasoning = (content) => content.filter((p) => p.type === "reasoning").map((p) => p.text ?? "").join("").trim(), responseToolCalls = (content) => content.filter((p) => p.type === "tool-call").map((p) => ({ id: p.id ?? "", toolName: p.name ?? "", args: p.params ?? {} }));
111
+ const encodedOf = (p) => p.encodedResult !== void 0 ? p.encodedResult : p.result;
112
+ export const responseToolResults = (content) => content.filter((p) => p.type === "tool-result").map((p) => ({
111
113
  id: p.id ?? "",
112
114
  toolName: p.name ?? "",
113
115
  ok: !(p.isFailure ?? !1),
114
116
  result: p.result
115
- })), extractUsage = (usage, content) => {
116
- const u = usage ?? {}, finishes = content.filter((p) => p.type === "finish"), finish = finishes.find((p) => {
117
- const fu = p.usage;
118
- return fu?.inputTokens !== void 0 || fu?.outputTokens !== void 0;
119
- }) ?? finishes[0], gm = finish?.metadata?.google?.usageMetadata, fu = finish?.usage, inputTokens = u.inputTokens ?? fu?.inputTokens ?? gm?.promptTokenCount ?? 0, outputTokens = u.outputTokens ?? fu?.outputTokens ?? gm?.candidatesTokenCount ?? 0, totalTokens = u.totalTokens ?? fu?.totalTokens ?? gm?.totalTokenCount ?? 0, cacheReadTokens = u.cachedInputTokens ?? fu?.cachedInputTokens ?? gm?.cachedContentTokenCount ?? 0, au = finish?.metadata?.anthropic?.usage;
120
- if (au !== void 0 && au !== null && typeof au === "object") {
121
- const cacheRead = au.cache_read_input_tokens ?? 0, cacheWrite = au.cache_creation_input_tokens ?? 0, fullInput = inputTokens + cacheRead + cacheWrite;
122
- return {
123
- inputTokens: fullInput,
124
- outputTokens,
125
- totalTokens: fullInput + outputTokens,
126
- cacheReadTokens: cacheRead
127
- };
128
- }
117
+ }));
118
+ const carriesUsage = (usage) => usage?.inputTokens?.total !== void 0 || usage?.outputTokens?.total !== void 0;
119
+ export const extractUsage = (usage, content) => {
120
+ const u = usage ?? {}, finishes = content.filter((p) => p.type === "finish"), finish = finishes.find((p) => carriesUsage(p.usage)) ?? finishes[0], gm = finish?.metadata?.google?.usageMetadata, fu = finish?.usage, inputTokens = u.inputTokens?.total ?? fu?.inputTokens?.total ?? gm?.promptTokenCount ?? 0, outputTokens = u.outputTokens?.total ?? fu?.outputTokens?.total ?? gm?.candidatesTokenCount ?? 0, totalTokens = carriesUsage(u) || carriesUsage(fu) ? inputTokens + outputTokens : gm?.totalTokenCount ?? 0, cacheReadTokens = u.inputTokens?.cacheRead ?? fu?.inputTokens?.cacheRead ?? gm?.cachedContentTokenCount ?? 0;
129
121
  return { inputTokens, outputTokens, totalTokens, cacheReadTokens };
130
122
  }, withUsageOnAssistant = (messages, usage, model = Option.none()) => {
131
123
  const at = messages.findIndex((m) => m.role === "assistant"), stamp = {
@@ -1,5 +1,5 @@
1
- import { FiberRef, Option } from "effect";
2
- export declare const CurrentModelCallPolicy: FiberRef.FiberRef<Option.Option<{
1
+ import { Context, Option } from "effect";
2
+ export declare const CurrentModelCallPolicy: Context.Reference<Option.Option<{
3
3
  readonly effort: "none" | "low" | "medium" | "high" | "xhigh" | "max";
4
4
  readonly maxOutputTokens?: number | undefined;
5
5
  }>>;
@@ -15,7 +15,7 @@ export declare const CurrentModelCallPolicy: FiberRef.FiberRef<Option.Option<{
15
15
  * deadlines, for work that finished in seconds).
16
16
  *
17
17
  * The LOOP sets this once the run has tool calls; adapters' empty-response
18
- * rejection consults it at call time. A FiberRef, not a port: ambient call
18
+ * rejection consults it at call time. A context reference, not a port: ambient call
19
19
  * metadata, exactly like {@link CurrentModelCallPolicy}.
20
20
  */
21
- export declare const CurrentEmptyResponseTolerance: FiberRef.FiberRef<boolean>;
21
+ export declare const CurrentEmptyResponseTolerance: Context.Reference<boolean>;
@@ -1,2 +1,2 @@
1
- import { FiberRef, GlobalValue, Option } from "effect";
2
- export const CurrentModelCallPolicy = GlobalValue.globalValue("@xandreed/core/CurrentModelCallPolicy", () => FiberRef.unsafeMake(Option.none())), CurrentEmptyResponseTolerance = GlobalValue.globalValue("@xandreed/core/CurrentEmptyResponseTolerance", () => FiberRef.unsafeMake(!1));
1
+ import { Context, Option } from "effect";
2
+ export const CurrentModelCallPolicy = Context.Reference("@xandreed/core/CurrentModelCallPolicy", { defaultValue: () => Option.none() }), CurrentEmptyResponseTolerance = Context.Reference("@xandreed/core/CurrentEmptyResponseTolerance", { defaultValue: () => !1 });
@@ -1,5 +1,5 @@
1
- import { FiberRef, Option } from "effect";
2
- export declare const CurrentPromptProvenance: FiberRef.FiberRef<Option.Option<{
1
+ import { Context, Option } from "effect";
2
+ export declare const CurrentPromptProvenance: Context.Reference<Option.Option<{
3
3
  readonly id: string & import("effect/Brand").Brand<"PromptId">;
4
4
  readonly version: string;
5
5
  readonly variant: string;
@@ -1,2 +1,2 @@
1
- import { FiberRef, GlobalValue, Option } from "effect";
2
- export const CurrentPromptProvenance = GlobalValue.globalValue("@xandreed/core/CurrentPromptProvenance", () => FiberRef.unsafeMake(Option.none()));
1
+ import { Context, Option } from "effect";
2
+ export const CurrentPromptProvenance = Context.Reference("@xandreed/core/CurrentPromptProvenance", { defaultValue: () => Option.none() });
@@ -1,3 +1,3 @@
1
- import { FiberRef, Option } from "effect";
1
+ import { Context, Option } from "effect";
2
2
  /** Correlation only; unset for work outside the agent loop. */
3
- export declare const CurrentAgentStep: FiberRef.FiberRef<Option.Option<number>>;
3
+ export declare const CurrentAgentStep: Context.Reference<Option.Option<number>>;
@@ -1,2 +1,2 @@
1
- import { FiberRef, GlobalValue, Option } from "effect";
2
- export const CurrentAgentStep = GlobalValue.globalValue("@xandreed/core/CurrentAgentStep", () => FiberRef.unsafeMake(Option.none()));
1
+ import { Context, Option } from "effect";
2
+ export const CurrentAgentStep = Context.Reference("@xandreed/core/CurrentAgentStep", { defaultValue: () => Option.none() });
@@ -11,11 +11,11 @@ const CHANNEL_BY_TYPE = {
11
11
  return [entry.part];
12
12
  return entry.text.length > 0 ? [{ type: entry.channel, text: entry.text }] : [];
13
13
  };
14
- export const foldStreamParts = (parts, onDelta) => Stream.runFoldEffect(parts, {
14
+ export const foldStreamParts = (parts, onDelta) => Stream.runFoldEffect(parts, () => ({
15
15
  entries: [],
16
16
  finishReason: Option.none(),
17
17
  usage: Option.none()
18
- }, (state, part) => {
18
+ }), (state, part) => {
19
19
  const p = part, type = p.type ?? "";
20
20
  if (type === "tool-params-start" || type === "tool-params-delta") {
21
21
  const id = p.id ?? "tool-params-1";
@@ -40,7 +40,7 @@ export const foldStreamParts = (parts, onDelta) => Stream.runFoldEffect(parts, {
40
40
  return delta.length > 0 ? onDelta({ channel, id, delta }).pipe(Effect.as(next)) : Effect.succeed(next);
41
41
  }
42
42
  if (type === "finish") {
43
- const usage = p.usage, carriesUsage = usage?.inputTokens !== void 0 || usage?.outputTokens !== void 0;
43
+ const usage = p.usage, carriesUsage = usage?.inputTokens?.total !== void 0 || usage?.outputTokens?.total !== void 0;
44
44
  return Effect.succeed({
45
45
  entries: [...state.entries, { kind: "part", part }],
46
46
  finishReason: Option.orElse(state.finishReason, () => Option.some(p.reason ?? "unknown")),
@@ -0,0 +1,17 @@
1
+ import { Schema } from "effect";
2
+ import type { JsonSchema } from "effect";
3
+ import type { Tool } from "effect/ai";
4
+ /**
5
+ * The JSON Schema a model sees for a schema: what it can write in JSON and the
6
+ * schema's decoder (the one a toolkit runs on tool calls) accepts.
7
+ *
8
+ * - Objects are closed: Effect's generator leaves them open by default.
9
+ * - An optional key may be absent, never `null`. Effect renders a schema
10
+ * through its JSON codec, where `undefined` is `null`, which the plain
11
+ * decoder rejects.
12
+ * - Numbers are finite, since JSON has no `NaN` or `Infinity`.
13
+ * - Definitions are inlined under `$defs`.
14
+ */
15
+ export declare const strictJsonSchema: (schema: Schema.Top) => JsonSchema.JsonSchema;
16
+ /** A tool's parameters as the model sees them. */
17
+ export declare const toolParametersSchema: (tool: Tool.Any) => JsonSchema.JsonSchema;
@@ -0,0 +1,25 @@
1
+ import { Match, Record, Schema, SchemaRepresentation } from "effect";
2
+ const finiteChecks = ((finite) => finite._tag === "Number" ? finite.checks : [])(SchemaRepresentation.toRepresentation(Schema.Finite.ast).representation), finiteIds = new Set(finiteChecks.flatMap((check) => check.representation === void 0 ? [] : [check.representation.id])), isFinite = (checks) => checks.some((check) => check.representation !== void 0 && finiteIds.has(check.representation.id)), absentWhenOptional = (isOptional, type) => {
3
+ if (!isOptional || type._tag !== "Union")
4
+ return type;
5
+ const types = type.types.filter((member) => member._tag !== "Undefined"), [only] = types;
6
+ return types.length === 1 && only !== void 0 && type.checks.length === 0 && type.annotations === void 0 ? only : { ...type, types };
7
+ }, modelFacing = (representation) => Match.value(representation).pipe(Match.tag("Number", (number) => isFinite(number.checks) ? number : { ...number, checks: [...finiteChecks, ...number.checks] }), Match.tag("Objects", (objects) => ({
8
+ ...objects,
9
+ propertySignatures: objects.propertySignatures.map((property) => ({
10
+ ...property,
11
+ type: absentWhenOptional(property.isOptional, modelFacing(property.type))
12
+ })),
13
+ indexSignatures: objects.indexSignatures.map((signature) => ({ ...signature, type: modelFacing(signature.type) }))
14
+ })), Match.tag("Arrays", (arrays) => ({
15
+ ...arrays,
16
+ elements: arrays.elements.map((element) => ({
17
+ ...element,
18
+ type: absentWhenOptional(element.isOptional, modelFacing(element.type))
19
+ })),
20
+ rest: arrays.rest.map(modelFacing)
21
+ })), Match.tag("Union", (union) => ({ ...union, types: union.types.map(modelFacing) })), Match.tag("Suspend", (suspend) => ({ ...suspend, thunk: modelFacing(suspend.thunk) })), Match.orElse((other) => other));
22
+ export const strictJsonSchema = (schema) => {
23
+ const document = SchemaRepresentation.toRepresentation(schema.ast), compiled = SchemaRepresentation.toJsonSchemaDocument({ representation: modelFacing(document.representation), references: Record.map(document.references, modelFacing) }, { onExcessProperty: "error" });
24
+ return Object.keys(compiled.definitions).length > 0 ? { ...compiled.schema, $defs: compiled.definitions } : compiled.schema;
25
+ }, toolParametersSchema = (tool) => strictJsonSchema(tool.parametersSchema);
@@ -0,0 +1 @@
1
+ export {};
@@ -1,34 +1,34 @@
1
- import { Tool, Toolkit } from "@effect/ai";
1
+ import { Tool, Toolkit } from "effect/ai";
2
2
  import { Context, Effect, Schema } from "effect";
3
3
  import { McpClient } from "../ports/mcp-client.port.js";
4
4
  import type { McpToolDescriptor } from "../ports/mcp-client.port.js";
5
5
  export declare const McpDescribe: Tool.Tool<"mcp_describe", {
6
6
  readonly parameters: Schema.Struct<{
7
- server: Schema.SchemaClass<string, string, never>;
8
- tool: Schema.SchemaClass<string, string, never>;
7
+ readonly server: Schema.String;
8
+ readonly tool: Schema.String;
9
9
  }>;
10
10
  readonly success: Schema.Struct<{
11
- server: typeof Schema.String;
12
- tool: typeof Schema.String;
13
- description: typeof Schema.String;
14
- inputSchema: typeof Schema.Unknown;
11
+ readonly server: Schema.String;
12
+ readonly tool: Schema.String;
13
+ readonly description: Schema.String;
14
+ readonly inputSchema: Schema.Unknown;
15
15
  }>;
16
16
  readonly failure: Schema.Struct<{
17
- error: typeof Schema.String;
18
- message: typeof Schema.String;
17
+ readonly error: Schema.String;
18
+ readonly message: Schema.String;
19
19
  }>;
20
20
  readonly failureMode: "return";
21
21
  }, never>;
22
22
  export declare const McpCall: Tool.Tool<"mcp_call", {
23
23
  readonly parameters: Schema.Struct<{
24
- server: Schema.SchemaClass<string, string, never>;
25
- tool: Schema.SchemaClass<string, string, never>;
26
- args: Schema.Record$<typeof Schema.String, typeof Schema.Unknown>;
24
+ readonly server: Schema.String;
25
+ readonly tool: Schema.String;
26
+ readonly args: Schema.$Record<Schema.String, Schema.Unknown>;
27
27
  }>;
28
- readonly success: typeof Schema.Unknown;
28
+ readonly success: Schema.Unknown;
29
29
  readonly failure: Schema.Struct<{
30
- error: typeof Schema.String;
31
- message: typeof Schema.String;
30
+ readonly error: Schema.String;
31
+ readonly message: Schema.String;
32
32
  }>;
33
33
  readonly failureMode: "return";
34
34
  }, never>;