@hunterzhu/pulse-runtime 0.5.1 → 0.6.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.
@@ -52,6 +52,8 @@ export declare class SessionStoragePolicy {
52
52
  pin(key: string): void;
53
53
  unpin(key: string): void;
54
54
  remove(key: string): boolean;
55
+ /** Only after the event archive has durably acknowledged these exact events. */
56
+ forgetArchivedEvents(ids: Iterable<string>): void;
55
57
  /** Replace one logical owner’s pins without double-counting repeated reconciliation. */
56
58
  replacePinSource(source: string, keys: Iterable<string>): void;
57
59
  /** Make a private copy suitable for validate-stage storage admission. Shallow: records are immutable and shared. */
@@ -123,6 +123,14 @@ export class SessionStoragePolicy {
123
123
  }
124
124
  return true;
125
125
  }
126
+ /** Only after the event archive has durably acknowledged these exact events. */
127
+ forgetArchivedEvents(ids) {
128
+ for (const id of ids) {
129
+ const record = this.records.get(`event:${id}`);
130
+ if (record?.kind === 'event' && record.storageState === 'persisted' && record.pinCount === 0)
131
+ this.unset(record.key);
132
+ }
133
+ }
126
134
  /** Replace one logical owner’s pins without double-counting repeated reconciliation. */
127
135
  replacePinSource(source, keys) {
128
136
  const next = new Set(keys);
@@ -619,6 +619,9 @@ function prepareLLMInput(state, lane, submission) {
619
619
  ? rawConversation
620
620
  : undefined;
621
621
  const conversationError = rawConversation !== undefined && conversation === undefined ? 'INVALID_LLM_CONVERSATION' : undefined;
622
+ const historySeqs = input.historySeqs;
623
+ if (historySeqs !== undefined && (!Array.isArray(historySeqs) || historySeqs.some(seq => typeof seq !== 'number' || !Number.isSafeInteger(seq) || seq < 1)))
624
+ return { error: 'INVALID_LLM_HISTORY_SELECTION' };
622
625
  const inputError = resultInputs.error ?? findingInputs.error ?? rejectedInputs.error ?? artifactInputs.error ?? eventInputs.error ?? conversationError;
623
626
  if (inputError)
624
627
  return { error: inputError };
@@ -628,7 +631,7 @@ function prepareLLMInput(state, lane, submission) {
628
631
  const agent = state.agents.get(lane.agentId);
629
632
  if (!agent)
630
633
  return { error: 'UNKNOWN_AGENT' };
631
- const projection = new ContextBuilder(state).build({ agent, lane, explicitContext: input.explicitContext === true, resultRefs, ...(artifactRefs.length ? { artifactRefs } : {}), eventIds: eventInputs.refs ?? [], ...(conversation === undefined || conversation.length === 0 ? {} : { conversation }), instruction, ...(typeof input.system === 'string' ? { system: input.system } : {}), ...(input.policy === undefined ? {} : { policy: input.policy }), ...(input.tools === undefined ? {} : { tools: input.tools }), toolSetId: typeof input.toolSetId === 'string' ? input.toolSetId : 'default' });
634
+ const projection = new ContextBuilder(state).build({ agent, lane, explicitContext: input.explicitContext === true, includeLaneHistory: input.includeLaneHistory === true, ...(Array.isArray(historySeqs) ? { historySeqs: historySeqs } : {}), resultRefs, ...(artifactRefs.length ? { artifactRefs } : {}), eventIds: eventInputs.refs ?? [], ...(conversation === undefined || conversation.length === 0 ? {} : { conversation }), instruction, ...(typeof input.system === 'string' ? { system: input.system } : {}), ...(input.policy === undefined ? {} : { policy: input.policy }), ...(input.tools === undefined ? {} : { tools: input.tools }), toolSetId: typeof input.toolSetId === 'string' ? input.toolSetId : 'default' });
632
635
  return { input: { ...input, request: projection } };
633
636
  }
634
637
  catch (cause) {
@@ -679,40 +682,42 @@ export function validateStep(state, laneId, output) {
679
682
  const seenToolCallIds = new Set();
680
683
  const seenCancelTargets = new Set();
681
684
  const cancelledSubtreeLanes = new Set();
682
- if (output.contextDelta !== undefined) {
683
- const deltaShapeError = validateContextDeltaShape(output.contextDelta);
685
+ if (output.laneContextDelta !== undefined && (output.contextDelta?.target !== 'global' || output.laneContextDelta?.target !== 'lane'))
686
+ return { rejection: error('INVALID_CONTEXT_DELTA_PAIR', 'A secondary lane delta requires a primary global delta') };
687
+ if (output.adoptCommittedContext && output.contextDelta?.target !== 'global')
688
+ return { rejection: error('INVALID_ADOPT_COMMITTED_CONTEXT', 'adoptCommittedContext requires a global ContextDelta') };
689
+ for (const delta of [output.contextDelta, output.laneContextDelta]) {
690
+ if (delta === undefined)
691
+ continue;
692
+ const deltaShapeError = validateContextDeltaShape(delta);
684
693
  if (deltaShapeError)
685
694
  return { rejection: error(deltaShapeError, 'ContextDelta shape is invalid') };
686
- const deltaPrivacyTaintError = validatePrivacyTaints(output.contextDelta.privacyTaints);
695
+ const deltaPrivacyTaintError = validatePrivacyTaints(delta.privacyTaints);
687
696
  if (deltaPrivacyTaintError)
688
697
  return { rejection: error(deltaPrivacyTaintError, 'ContextDelta privacy taints are invalid') };
689
- const deltaDerived = derivedPrivacy(state, lane, output.contextDelta.derivedFrom ?? []);
698
+ const deltaDerived = derivedPrivacy(state, lane, delta.derivedFrom ?? []);
690
699
  if (deltaDerived.error)
691
700
  return { rejection: error(deltaDerived.error, 'ContextDelta provenance references an unknown or invisible result') };
692
- if (output.contextDelta.privacy !== undefined && deltaDerived.privacy !== undefined && privacyRank(output.contextDelta.privacy) < privacyRank(deltaDerived.privacy))
701
+ if (delta.privacy !== undefined && deltaDerived.privacy !== undefined && privacyRank(delta.privacy) < privacyRank(deltaDerived.privacy))
693
702
  return { rejection: error('PRIVACY_DOWNGRADE_WITHOUT_PROOF', 'ContextDelta privacy cannot be broader than its sources') };
694
- if (output.contextDelta.target === 'global' && !output.contextDelta.proposal && lane.ownerLaneId !== undefined)
703
+ if (delta.target === 'global' && !delta.proposal && lane.ownerLaneId !== undefined)
695
704
  return { rejection: error('GLOBAL_CONTEXT_WRITE_NOT_AUTHORIZED', 'Only the root Lane may commit Global Context directly.') };
696
- if (output.contextDelta.target !== 'global' && output.contextDelta.proposal)
705
+ if (delta.target !== 'global' && delta.proposal)
697
706
  return { rejection: error('INVALID_MERGE_PROPOSAL', 'Only Global Context deltas may be proposals.') };
698
- const applied = applyContextDelta(state, workingLane, output.contextDelta, mutations, output.contextDelta.proposal ? `proposal-${proposalCounter++}` : undefined);
707
+ const applied = applyContextDelta(state, workingLane, delta, mutations, delta.proposal ? `proposal-${proposalCounter++}` : undefined);
699
708
  if (applied.error)
700
709
  return { rejection: error(applied.error, 'ContextDelta rejected') };
701
- if (output.contextDelta.target === 'lane') {
710
+ if (delta.target === 'lane') {
702
711
  const nextValue = mutations[mutations.length - 1];
703
712
  if (nextValue?.op === 'setLaneContext')
704
713
  workingLane.context = { ...workingLane.context, state: nextValue.value, version: applied.nextVersion, ...(nextValue.history === undefined ? {} : { history: structuredClone(nextValue.history) }), ...(nextValue.metadata === undefined ? {} : structuredClone(nextValue.metadata)) };
705
714
  }
706
- if (output.adoptCommittedContext && output.contextDelta.target !== 'global')
707
- return { rejection: error('INVALID_ADOPT_COMMITTED_CONTEXT', 'adoptCommittedContext requires a global ContextDelta') };
708
- if (output.contextDelta.proposal && output.adoptCommittedContext)
715
+ if (delta.proposal && output.adoptCommittedContext)
709
716
  return { rejection: error('INVALID_ADOPT_COMMITTED_CONTEXT', 'proposals cannot be adopted in the same transaction') };
710
- if (output.contextDelta.target === 'global' && output.adoptCommittedContext)
717
+ if (delta.target === 'global' && output.adoptCommittedContext)
711
718
  workingLane.contextSnapshotVersion = applied.nextVersion;
712
719
  }
713
- else if (output.adoptCommittedContext)
714
- return { rejection: error('INVALID_ADOPT_COMMITTED_CONTEXT', 'adoptCommittedContext requires a ContextDelta') };
715
- const compactRequested = output.contextDelta?.target === 'lane' && output.contextDelta.ops.some((op) => op.op === 'compact_history');
720
+ const compactRequested = [output.contextDelta, output.laneContextDelta].some(delta => delta?.target === 'lane' && delta.ops.some(op => op.op === 'compact_history'));
716
721
  // ReAct can discover pressure only after consuming a model result. Permit
717
722
  // the two-step compaction handoff and its summary request to cross the hard
718
723
  // threshold; the following compact_history delta removes the old records.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hunterzhu/pulse-runtime",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "license": "PolyForm-Noncommercial-1.0.0",
5
5
  "repository": {
6
6
  "type": "git",