@inixiative/foundry-core 0.1.0 → 0.3.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.
@@ -1,4 +1,4 @@
1
- import type { OwnershipScope } from "./scope";
1
+ import type { OwnershipScope } from './scope';
2
2
 
3
3
  /**
4
4
  * A source that can provide content to a layer.
@@ -10,7 +10,7 @@ import type { OwnershipScope } from "./scope";
10
10
  * record) cannot be carried and no retrieval exists. The executor refuses
11
11
  * provider execution while any included layer reports it.
12
12
  */
13
- export const REQUIRED_CONTEXT_BLOCKED = "required-context-blocked";
13
+ export const REQUIRED_CONTEXT_BLOCKED = 'required-context-blocked';
14
14
 
15
15
  /** Logical request identity, independent of dispatches and native sessions. */
16
16
  export interface LogicalMessageIdentity extends OwnershipScope {
@@ -19,15 +19,25 @@ export interface LogicalMessageIdentity extends OwnershipScope {
19
19
  }
20
20
 
21
21
  /** Own only the identity fields; callers cannot change a pending load by reference. */
22
- export function copyMessageIdentity(identity: LogicalMessageIdentity | undefined): LogicalMessageIdentity | undefined {
23
- return identity?.messageId && identity.threadId ? Object.freeze({
24
- messageId: identity.messageId, threadId: identity.threadId,
25
- ...(identity.projectId ? { projectId: identity.projectId } : {}),
26
- }) : undefined;
22
+ export function copyMessageIdentity(
23
+ identity: LogicalMessageIdentity | undefined,
24
+ ): LogicalMessageIdentity | undefined {
25
+ return identity?.messageId && identity.threadId
26
+ ? Object.freeze({
27
+ messageId: identity.messageId,
28
+ threadId: identity.threadId,
29
+ ...(identity.projectId ? { projectId: identity.projectId } : {}),
30
+ })
31
+ : undefined;
27
32
  }
28
33
 
29
- function sameMessage(a: LogicalMessageIdentity | undefined, b: LogicalMessageIdentity | undefined): boolean {
30
- return a?.messageId === b?.messageId && a?.threadId === b?.threadId && a?.projectId === b?.projectId;
34
+ function sameMessage(
35
+ a: LogicalMessageIdentity | undefined,
36
+ b: LogicalMessageIdentity | undefined,
37
+ ): boolean {
38
+ return (
39
+ a?.messageId === b?.messageId && a?.threadId === b?.threadId && a?.projectId === b?.projectId
40
+ );
31
41
  }
32
42
 
33
43
  export interface SourceLoadHint {
@@ -49,17 +59,27 @@ export interface SourceSelectionReport {
49
59
  readonly chars: number;
50
60
  readonly kind?: string;
51
61
  readonly timestamp?: number;
52
- readonly truncated?: boolean; ranges?: Array<readonly [number, number]>;
62
+ readonly truncated?: boolean;
63
+ ranges?: Array<readonly [number, number]>;
53
64
  readonly matched?: string[];
54
65
  }>;
55
- readonly omitted: Array<{ readonly id: string; readonly reason: string; readonly chars: number; readonly kind?: string;
56
- readonly excludedFor?: LogicalMessageIdentity }>;
66
+ readonly omitted: Array<{
67
+ readonly id: string;
68
+ readonly reason: string;
69
+ readonly chars: number;
70
+ readonly kind?: string;
71
+ readonly excludedFor?: LogicalMessageIdentity;
72
+ }>;
57
73
  /** Records the reader was allowed to see. */
58
74
  readonly considered: number;
59
75
  /** Everything retained in the source for this reader, injected or not. */
60
76
  readonly retained: { readonly count: number; readonly chars: number };
61
77
  readonly budget: { readonly chars: number; readonly used: number; readonly exceeded: boolean };
62
- readonly conflicts: Array<{ readonly kind: string; readonly ids: string[]; readonly detail: string }>;
78
+ readonly conflicts: Array<{
79
+ readonly kind: string;
80
+ readonly ids: string[];
81
+ readonly detail: string;
82
+ }>;
63
83
  readonly focus?: { readonly hash: string; readonly terms: number };
64
84
  readonly currentMessage?: LogicalMessageIdentity;
65
85
  }
@@ -100,10 +120,13 @@ export interface LayerSelectionState {
100
120
  * layer predates this metadata or was never classified; consumers then fall back
101
121
  * to their legacy id heuristic and must not present that as verified provenance.
102
122
  */
103
- export type LayerSegment = "domain-knowledge" | "thread-knowledge";
104
- export const LAYER_SEGMENTS: readonly LayerSegment[] = Object.freeze(["domain-knowledge", "thread-knowledge"]);
123
+ export type LayerSegment = 'domain-knowledge' | 'thread-knowledge';
124
+ export const LAYER_SEGMENTS: readonly LayerSegment[] = Object.freeze([
125
+ 'domain-knowledge',
126
+ 'thread-knowledge',
127
+ ]);
105
128
  export function isLayerSegment(value: unknown): value is LayerSegment {
106
- return typeof value === "string" && (LAYER_SEGMENTS as readonly string[]).includes(value);
129
+ return typeof value === 'string' && (LAYER_SEGMENTS as readonly string[]).includes(value);
107
130
  }
108
131
 
109
132
  /**
@@ -127,7 +150,7 @@ export interface LayerDefinition {
127
150
  /** Instruction explaining how this layer's content should be used. */
128
151
  prompt?: string;
129
152
  /** When this layer is activated: always, on-demand, or conditionally. */
130
- activation?: "always" | "on-demand" | "conditional";
153
+ activation?: 'always' | 'on-demand' | 'conditional';
131
154
  /** Who can write to instances of this layer. Undefined = anyone. */
132
155
  writers?: string[];
133
156
  }
@@ -221,7 +244,7 @@ export interface VersionLog<T = unknown> {
221
244
  /** What is being versioned: "layer:conventions", "agent:librarian", etc. */
222
245
  subject: string;
223
246
  /** "definition" or "instance". */
224
- scope: "definition" | "instance";
247
+ scope: 'definition' | 'instance';
225
248
  /** Ordered version entries, newest last. */
226
249
  entries: VersionEntry<T>[];
227
250
  }
@@ -244,7 +267,7 @@ export interface ContextLayerConfig {
244
267
  segment?: LayerSegment;
245
268
  }
246
269
 
247
- export type LayerState = "cold" | "warming" | "warm" | "stale" | "compressing";
270
+ export type LayerState = 'cold' | 'warming' | 'warm' | 'stale' | 'compressing';
248
271
 
249
272
  /** Emitted on any write to a layer instance. The Session Store captures these. */
250
273
  export interface LayerMutationEvent {
@@ -278,9 +301,9 @@ export class ContextLayer {
278
301
  */
279
302
  readonly segment: LayerSegment | undefined;
280
303
 
281
- private _content: string = "";
282
- private _state: LayerState = "cold";
283
- private _hash: string = "";
304
+ private _content: string = '';
305
+ private _state: LayerState = 'cold';
306
+ private _hash: string = '';
284
307
  private _lastWarmed: number | null = null;
285
308
  private _lastAccessed: number | null = null;
286
309
  private _sources: ContextSource[];
@@ -293,8 +316,7 @@ export class ContextLayer {
293
316
  private _version?: LayerVersionMark;
294
317
  private _warmingPromise: Promise<void> | null = null;
295
318
 
296
- private _listeners: Array<(state: LayerState, layer: ContextLayer) => void> =
297
- [];
319
+ private _listeners: Array<(state: LayerState, layer: ContextLayer) => void> = [];
298
320
 
299
321
  /**
300
322
  * Mutation listeners — called on any write to this layer (content, state).
@@ -302,9 +324,7 @@ export class ContextLayer {
302
324
  * Unlike onStateChange (which only fires on state transitions), this fires
303
325
  * on content changes and any other mutation.
304
326
  */
305
- private _mutationListeners: Array<
306
- (event: LayerMutationEvent) => void
307
- > = [];
327
+ private _mutationListeners: Array<(event: LayerMutationEvent) => void> = [];
308
328
 
309
329
  constructor(config: ContextLayerConfig) {
310
330
  this.id = config.id;
@@ -315,7 +335,8 @@ export class ContextLayer {
315
335
  this._maxTokens = config.maxTokens ?? config.definition?.maxTokens;
316
336
  this._prompt = config.prompt ?? config.definition?.prompt;
317
337
  const segment = config.segment ?? config.definition?.segment;
318
- if (segment !== undefined && !isLayerSegment(segment)) throw new Error(`Unknown layer segment "${String(segment)}" for layer "${config.id}"`);
338
+ if (segment !== undefined && !isLayerSegment(segment))
339
+ throw new Error(`Unknown layer segment "${String(segment)}" for layer "${config.id}"`);
319
340
  this.segment = segment;
320
341
  }
321
342
 
@@ -379,7 +400,7 @@ export class ContextLayer {
379
400
  });
380
401
  copy._focus = this._focus;
381
402
  copy._currentMessage = this._currentMessage;
382
- if (this._state !== "cold") {
403
+ if (this._state !== 'cold') {
383
404
  copy.restoreInstance(this.snapshotInstance());
384
405
  }
385
406
  return copy;
@@ -393,21 +414,29 @@ export class ContextLayer {
393
414
  this._lastAccessed = snapshot.lastAccessed;
394
415
  this._selection = snapshot.selection ? structuredClone(snapshot.selection) : undefined;
395
416
  this._version = undefined;
396
- if (snapshot.definitionId === (this.definition?.id ?? this.id)
397
- && (!this.owner || snapshot.threadId === this.owner.threadId && snapshot.projectId === this.owner.projectId)
398
- && snapshot.version && this._validVersion(snapshot.version)) this._version = Object.freeze({ ...snapshot.version });
399
- const state = snapshot.state === "warming" ? "warm" : snapshot.state;
417
+ if (
418
+ snapshot.definitionId === (this.definition?.id ?? this.id) &&
419
+ (!this.owner ||
420
+ (snapshot.threadId === this.owner.threadId &&
421
+ snapshot.projectId === this.owner.projectId)) &&
422
+ snapshot.version &&
423
+ this._validVersion(snapshot.version)
424
+ )
425
+ this._version = Object.freeze({ ...snapshot.version });
426
+ const state = snapshot.state === 'warming' ? 'warm' : snapshot.state;
400
427
  // Restored content carries the focus it was selected for. If this layer is now focused
401
428
  // elsewhere, the content is real but answers another message: stale, not warm.
402
- this._setState(state === "warm" && this._focusMismatch() ? "stale" : state);
429
+ this._setState(state === 'warm' && this._focusMismatch() ? 'stale' : state);
403
430
  }
404
431
 
405
432
  /** True when a focusable source's recorded selection focus differs from the current focus. */
406
433
  private _focusMismatch(): boolean {
407
434
  if (!this._sources.some((source) => source.focusable)) return false;
408
435
  const current = this._focus ? computeHash(this._focus) : undefined;
409
- return (this._selection?.focusHash ?? undefined) !== current ||
410
- !sameMessage(this._selection?.currentMessage, this._currentMessage);
436
+ return (
437
+ (this._selection?.focusHash ?? undefined) !== current ||
438
+ !sameMessage(this._selection?.currentMessage, this._currentMessage)
439
+ );
411
440
  }
412
441
 
413
442
  // -- Focus --
@@ -430,8 +459,8 @@ export class ContextLayer {
430
459
  // Omitting identity deliberately clears it: direct/background work must
431
460
  // never inherit the preceding request just because its text is identical.
432
461
  this._currentMessage = identity;
433
- if (this._sources.some((source) => source.focusable) && this._state === "warm") {
434
- this._setState("stale");
462
+ if (this._sources.some((source) => source.focusable) && this._state === 'warm') {
463
+ this._setState('stale');
435
464
  }
436
465
  }
437
466
 
@@ -459,12 +488,12 @@ export class ContextLayer {
459
488
  /** Check if this layer has become stale. Triggers state transition if so. */
460
489
  checkStaleness(): LayerState {
461
490
  if (
462
- this._state === "warm" &&
491
+ this._state === 'warm' &&
463
492
  this._staleness !== undefined &&
464
493
  this._lastWarmed !== null &&
465
494
  Date.now() - this._lastWarmed > this._staleness
466
495
  ) {
467
- this._setState("stale");
496
+ this._setState('stale');
468
497
  }
469
498
  return this._state;
470
499
  }
@@ -478,12 +507,12 @@ export class ContextLayer {
478
507
  }
479
508
 
480
509
  get isWarm(): boolean {
481
- return this._state === "warm";
510
+ return this._state === 'warm';
482
511
  }
483
512
 
484
513
  get isStale(): boolean {
485
514
  this.checkStaleness();
486
- return this._state === "stale";
515
+ return this._state === 'stale';
487
516
  }
488
517
 
489
518
  // -- Lifecycle --
@@ -502,7 +531,7 @@ export class ContextLayer {
502
531
 
503
532
  private async _doWarm(): Promise<void> {
504
533
  const previousState = this._state;
505
- this._setState("warming");
534
+ this._setState('warming');
506
535
 
507
536
  try {
508
537
  // A load is labelled with the focus it was asked for. If a focusable source's focus moves
@@ -512,69 +541,94 @@ export class ContextLayer {
512
541
  for (let attempt = 0; ; attempt++) {
513
542
  const focus = this._focus;
514
543
  const currentMessage = this._currentMessage;
515
- const hint: SourceLoadHint | undefined = focus || currentMessage
516
- ? Object.freeze({ ...(focus ? { focus } : {}), ...(currentMessage ? { currentMessage } : {}) }) : undefined;
544
+ const hint: SourceLoadHint | undefined =
545
+ focus || currentMessage
546
+ ? Object.freeze({
547
+ ...(focus ? { focus } : {}),
548
+ ...(currentMessage ? { currentMessage } : {}),
549
+ })
550
+ : undefined;
517
551
  const parts: string[] = [];
518
- const reports: LayerSelectionState["sources"] = [];
552
+ const reports: LayerSelectionState['sources'] = [];
519
553
  for (const source of this._sources) {
520
554
  parts.push(await source.load(hint));
521
555
  const report = source.report?.();
522
556
  if (report) reports.push({ sourceId: source.id, report: structuredClone(report) });
523
557
  }
524
558
 
525
- this._content = parts.join("\n\n");
559
+ this._content = parts.join('\n\n');
526
560
  this._hash = computeHash(this._content);
527
561
  this._version = undefined; // even identical unversioned source bytes are a new write
528
562
  this._selection = reports.length
529
- ? { ...(focus ? { focusHash: computeHash(focus) } : {}), ...(currentMessage ? { currentMessage } : {}), sources: reports }
563
+ ? {
564
+ ...(focus ? { focusHash: computeHash(focus) } : {}),
565
+ ...(currentMessage ? { currentMessage } : {}),
566
+ sources: reports,
567
+ }
530
568
  : undefined;
531
569
  this._lastWarmed = Date.now();
532
570
 
533
- const focusMoved = (this._focus !== focus || !sameMessage(this._currentMessage, currentMessage)) &&
571
+ const focusMoved =
572
+ (this._focus !== focus || !sameMessage(this._currentMessage, currentMessage)) &&
534
573
  this._sources.some((source) => source.focusable);
535
- if (!focusMoved) { this._setState("warm"); return; }
536
- if (attempt >= 2) { this._setState("stale"); return; }
574
+ if (!focusMoved) {
575
+ this._setState('warm');
576
+ return;
577
+ }
578
+ if (attempt >= 2) {
579
+ this._setState('stale');
580
+ return;
581
+ }
537
582
  }
538
583
  } catch (err) {
539
584
  // Revert to previous state so the layer isn't stuck in "warming". Content and selection
540
585
  // provenance are whatever the last successful load left; a stale label stays stale.
541
- const reverted = previousState === "warming" ? "cold" : previousState;
586
+ const reverted = previousState === 'warming' ? 'cold' : previousState;
542
587
  // Content from the last good load stays, but it answers the focus it was
543
588
  // selected for. If the focus has moved since, warm would be a lie.
544
- this._setState(reverted === "warm" && this._focusMismatch() ? "stale" : reverted);
589
+ this._setState(reverted === 'warm' && this._focusMismatch() ? 'stale' : reverted);
545
590
  throw err;
546
591
  }
547
592
  }
548
593
 
549
- set(content: string, author: string = "system"): void {
594
+ set(content: string, author: string = 'system'): void {
550
595
  const previous = this._content;
551
596
  this._content = content;
552
597
  this._hash = computeHash(content);
553
598
  this._selection = undefined; // manual content has no selection provenance
554
599
  this._version = undefined; // a new write carries no version until its owner marks it
555
600
  this._lastWarmed = Date.now();
556
- this._setState("warm");
601
+ this._setState('warm');
557
602
  if (previous !== content) {
558
- this._emitMutation("content", previous, content, author);
603
+ this._emitMutation('content', previous, content, author);
559
604
  }
560
605
  }
561
606
 
562
607
  invalidate(): void {
563
- if (this._state !== "cold") {
564
- this._setState("stale");
608
+ if (this._state !== 'cold') {
609
+ this._setState('stale');
565
610
  }
566
611
  }
567
612
 
568
613
  private _validVersion(mark: LayerVersionMark): boolean {
569
- return Number.isSafeInteger(mark.revision) && mark.revision >= 0 && mark.hash === this._hash && mark.hash === computeHash(this._content)
570
- && (mark.domain === undefined || typeof mark.domain === "string" && mark.domain.length > 0)
571
- && [mark.threadId, mark.projectId, mark.author].every(value => value === undefined || typeof value === "string" && value.length > 0)
572
- && (!this.owner || mark.threadId === this.owner.threadId && mark.projectId === this.owner.projectId);
614
+ return (
615
+ Number.isSafeInteger(mark.revision) &&
616
+ mark.revision >= 0 &&
617
+ mark.hash === this._hash &&
618
+ mark.hash === computeHash(this._content) &&
619
+ (mark.domain === undefined || (typeof mark.domain === 'string' && mark.domain.length > 0)) &&
620
+ [mark.threadId, mark.projectId, mark.author].every(
621
+ (value) => value === undefined || (typeof value === 'string' && value.length > 0),
622
+ ) &&
623
+ (!this.owner ||
624
+ (mark.threadId === this.owner.threadId && mark.projectId === this.owner.projectId))
625
+ );
573
626
  }
574
627
 
575
628
  /** A rejected mark never replaces the current valid owner mark. */
576
629
  markVersion(mark: LayerVersionMark): void {
577
- if (!mark || !this._validVersion(mark)) throw new Error("Layer version mark does not describe the current content and owner");
630
+ if (!mark || !this._validVersion(mark))
631
+ throw new Error('Layer version mark does not describe the current content and owner');
578
632
  this._version = Object.freeze({ ...mark });
579
633
  }
580
634
 
@@ -583,12 +637,12 @@ export class ContextLayer {
583
637
  }
584
638
 
585
639
  clear(): void {
586
- this._content = "";
587
- this._hash = "";
640
+ this._content = '';
641
+ this._hash = '';
588
642
  this._lastWarmed = null;
589
643
  this._selection = undefined;
590
644
  this._version = undefined;
591
- this._setState("cold");
645
+ this._setState('cold');
592
646
  }
593
647
 
594
648
  addSource(source: ContextSource): void {
@@ -604,9 +658,7 @@ export class ContextLayer {
604
658
 
605
659
  // -- Observation --
606
660
 
607
- onStateChange(
608
- listener: (state: LayerState, layer: ContextLayer) => void
609
- ): () => void {
661
+ onStateChange(listener: (state: LayerState, layer: ContextLayer) => void): () => void {
610
662
  this._listeners.push(listener);
611
663
  return () => {
612
664
  const idx = this._listeners.indexOf(listener);
@@ -624,7 +676,7 @@ export class ContextLayer {
624
676
  const previous = this._staleness;
625
677
  this._staleness = value;
626
678
  if (previous !== value) {
627
- this._emitMutation("staleness", previous, value, "system");
679
+ this._emitMutation('staleness', previous, value, 'system');
628
680
  }
629
681
  }
630
682
 
@@ -644,7 +696,7 @@ export class ContextLayer {
644
696
  const previous = this._prompt;
645
697
  this._prompt = value;
646
698
  if (previous !== value) {
647
- this._emitMutation("prompt", previous, value, "system");
699
+ this._emitMutation('prompt', previous, value, 'system');
648
700
  }
649
701
  }
650
702
 
@@ -670,12 +722,7 @@ export class ContextLayer {
670
722
 
671
723
  // -- Internal --
672
724
 
673
- private _emitMutation(
674
- field: string,
675
- previous: unknown,
676
- current: unknown,
677
- author: string,
678
- ): void {
725
+ private _emitMutation(field: string, previous: unknown, current: unknown, author: string): void {
679
726
  if (this._mutationListeners.length === 0) return;
680
727
  const event: LayerMutationEvent = {
681
728
  layerId: this.id,
@@ -701,6 +748,6 @@ export class ContextLayer {
701
748
  listener(state, this);
702
749
  }
703
750
  // Also emit as mutation for instance history
704
- this._emitMutation("state", previous, state, "system");
751
+ this._emitMutation('state', previous, state, 'system');
705
752
  }
706
753
  }
@@ -1,5 +1,5 @@
1
- import { ContextLayer, computeHash, type LayerState } from "./context-layer";
2
- import type { OwnershipScope } from "./scope";
1
+ import { type ContextLayer, computeHash, type LayerState } from './context-layer';
2
+ import type { OwnershipScope } from './scope';
3
3
 
4
4
  export type LayerFilter = (layer: ContextLayer) => boolean;
5
5
 
@@ -25,7 +25,7 @@ export interface ContextStackView {
25
25
 
26
26
  /** A single block in the assembled prompt output. */
27
27
  export interface PromptBlock {
28
- readonly role: "system" | "layer" | "content";
28
+ readonly role: 'system' | 'layer' | 'content';
29
29
  readonly id?: string;
30
30
  readonly text: string;
31
31
  /**
@@ -128,16 +128,12 @@ export class ContextStack {
128
128
  async warmAll(): Promise<void> {
129
129
  // Check staleness before deciding what to warm
130
130
  for (const l of this._layers) l.checkStaleness();
131
- await Promise.all(
132
- this._layers.filter((l) => !l.isWarm).map((l) => l.warm())
133
- );
131
+ await Promise.all(this._layers.filter((l) => !l.isWarm).map((l) => l.warm()));
134
132
  }
135
133
 
136
134
  async refresh(): Promise<void> {
137
135
  for (const l of this._layers) l.checkStaleness();
138
- await Promise.all(
139
- this._layers.filter((l) => l.isStale).map((l) => l.warm())
140
- );
136
+ await Promise.all(this._layers.filter((l) => l.isStale).map((l) => l.warm()));
141
137
  }
142
138
 
143
139
  // -- Merging --
@@ -147,7 +143,7 @@ export class ContextStack {
147
143
  return layers
148
144
  .filter((l) => l.isWarm && l.content.length > 0)
149
145
  .map((l) => l.content)
150
- .join("\n\n");
146
+ .join('\n\n');
151
147
  }
152
148
 
153
149
  slice(filter: LayerFilter): string {
@@ -169,7 +165,7 @@ export class ContextStack {
169
165
  const blocks: PromptBlock[] = [];
170
166
 
171
167
  if (agentPrompt) {
172
- blocks.push({ role: "system", text: agentPrompt });
168
+ blocks.push({ role: 'system', text: agentPrompt });
173
169
  }
174
170
 
175
171
  const layers = filter ? this._layers.filter(filter) : this._layers;
@@ -179,14 +175,19 @@ export class ContextStack {
179
175
 
180
176
  if (layer.prompt) {
181
177
  // A layer prompt is always an instruction; it never inherits the content segment.
182
- blocks.push({ role: "layer", id: layer.id, text: layer.prompt });
178
+ blocks.push({ role: 'layer', id: layer.id, text: layer.prompt });
183
179
  }
184
180
  // The content block carries the layer's declared segment when it has one; legacy
185
181
  // layers leave it unset and the artifact builder falls back to its id heuristic.
186
- blocks.push({ role: "content", id: layer.id, text: layer.content, ...(layer.segment ? { segment: layer.segment } : {}) });
182
+ blocks.push({
183
+ role: 'content',
184
+ id: layer.id,
185
+ text: layer.content,
186
+ ...(layer.segment ? { segment: layer.segment } : {}),
187
+ });
187
188
  }
188
189
 
189
- const text = blocks.map((b) => b.text).join("\n\n");
190
+ const text = blocks.map((b) => b.text).join('\n\n');
190
191
  return { blocks, text };
191
192
  }
192
193
 
@@ -1,11 +1,13 @@
1
1
  /** Credential references belong in configuration; secrets stay behind the resolver. */
2
- export type CredentialReference = { type: 'managed'; id: string } | { type: 'kingdom-runtime' };
2
+ /** `kingdom-runtime` names the paired Kingdom by the destination's origin plus the owner it was paired as. */
3
+ export type CredentialReference =
4
+ | { type: 'managed'; id: string }
5
+ | { type: 'kingdom-runtime'; owner: string };
3
6
 
4
7
  export interface CredentialScope {
5
8
  service: string;
6
9
  url: string;
7
10
  projectId: string;
8
- kastleId?: string;
9
11
  }
10
12
 
11
13
  export interface CredentialResolver {
package/src/decider.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { computeHash } from "./context-layer";
2
- import { BaseAgent, type AgentConfig, type ExecutionResult } from "./base-agent";
3
- import type { ContextStack, LayerFilter } from "./context-stack";
4
- import type { ExecuteMeta } from "./executor";
1
+ import { type AgentConfig, BaseAgent, type ExecutionResult } from './base-agent';
2
+ import { computeHash } from './context-layer';
3
+ import type { ContextStack, LayerFilter } from './context-stack';
4
+ import type { ExecuteMeta } from './executor';
5
5
 
6
6
  export interface Decision<T = unknown> {
7
7
  readonly value: T;
@@ -17,11 +17,10 @@ export interface Decision<T = unknown> {
17
17
  export type DecideHandler<TPayload, TDecision> = (
18
18
  context: string,
19
19
  payload: TPayload,
20
- meta?: ExecuteMeta
20
+ meta?: ExecuteMeta,
21
21
  ) => Promise<Decision<TDecision>>;
22
22
 
23
- export interface DeciderConfig<TPayload = unknown, TDecision = unknown>
24
- extends AgentConfig {
23
+ export interface DeciderConfig<TPayload = unknown, TDecision = unknown> extends AgentConfig {
25
24
  handler: DecideHandler<TPayload, TDecision>;
26
25
  }
27
26
 
@@ -51,7 +50,7 @@ export class Decider<TPayload = unknown, TDecision = unknown> extends BaseAgent<
51
50
  async run(
52
51
  payload: TPayload,
53
52
  filterOverride?: LayerFilter,
54
- meta?: ExecuteMeta
53
+ meta?: ExecuteMeta,
55
54
  ): Promise<ExecutionResult<Decision<TDecision>>> {
56
55
  const context = this.getContextWith(filterOverride);
57
56
  const contextHash = computeHash(context);