@jini-ai/chat 0.5.4 → 0.5.5

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 (122) hide show
  1. package/CHANGELOG.md +4 -0
  2. package/dist/core/ag-ui/entry.d.ts +33 -0
  3. package/dist/core/ag-ui/entry.js +80 -0
  4. package/dist/core/agentic/chat-capabilities.d.ts +2 -4
  5. package/dist/core/agentic/index.d.ts +4 -17
  6. package/dist/core/agentic/index.js +4 -17
  7. package/dist/core/assistant-client/a2ui-action-poster.d.ts +46 -0
  8. package/dist/core/assistant-client/a2ui-action-poster.js +113 -0
  9. package/dist/core/assistant-client/ag-ui-transport.d.ts +68 -0
  10. package/dist/core/assistant-client/ag-ui-transport.js +147 -0
  11. package/dist/core/assistant-client/attachment-liveness.d.ts +37 -0
  12. package/dist/core/assistant-client/attachment-liveness.js +93 -0
  13. package/dist/core/assistant-client/durable-run-subscription.d.ts +39 -0
  14. package/dist/core/assistant-client/durable-run-subscription.js +96 -0
  15. package/dist/core/assistant-client/entry.d.ts +13 -0
  16. package/dist/core/assistant-client/entry.js +7 -0
  17. package/dist/core/assistant-client/messages.d.ts +73 -0
  18. package/dist/core/assistant-client/messages.js +68 -0
  19. package/dist/core/assistant-client/transport.d.ts +164 -0
  20. package/dist/core/assistant-client/transport.js +734 -0
  21. package/dist/core/browser/page-actions.d.ts +121 -0
  22. package/dist/core/browser/page-actions.js +113 -0
  23. package/dist/core/durable-runs/continuation.d.ts +18 -0
  24. package/dist/core/durable-runs/continuation.js +61 -0
  25. package/dist/core/durable-runs/ledger-contracts.d.ts +53 -0
  26. package/dist/core/durable-runs/ledger-contracts.js +2 -0
  27. package/dist/core/durable-runs/policies.d.ts +6 -0
  28. package/dist/core/durable-runs/policies.js +6 -0
  29. package/dist/core/durable-runs/ports.d.ts +123 -0
  30. package/dist/core/durable-runs/ports.js +2 -0
  31. package/dist/core/durable-runs/run-contracts.d.ts +17 -0
  32. package/dist/core/durable-runs/run-contracts.js +2 -0
  33. package/dist/core/durable-runs/run-lock.d.ts +4 -0
  34. package/dist/core/durable-runs/run-lock.js +4 -0
  35. package/dist/core/index.d.ts +6 -19
  36. package/dist/core/index.js +6 -19
  37. package/dist/core/messages.d.ts +2 -0
  38. package/dist/core/question-form/format.d.ts +0 -9
  39. package/dist/core/question-form/format.js +16 -2
  40. package/dist/core/question-form/normalize.js +1 -0
  41. package/dist/core/question-form/secret-answers.d.ts +38 -0
  42. package/dist/core/question-form/secret-answers.js +45 -0
  43. package/dist/core/question-form/types.d.ts +2 -0
  44. package/dist/core/question-form.d.ts +1 -0
  45. package/dist/core/question-form.js +1 -0
  46. package/dist/core/run-events/entry.d.ts +208 -16
  47. package/dist/core/run-events/entry.js +273 -39
  48. package/dist/core/transcript.d.ts +3 -2
  49. package/dist/core/transcript.js +19 -3
  50. package/dist/core/user-text-redaction.d.ts +56 -0
  51. package/dist/core/user-text-redaction.js +89 -0
  52. package/dist/embed-widget/browser/page-actions.d.ts +1 -120
  53. package/dist/embed-widget/browser/page-actions.js +2 -112
  54. package/dist/embed-widget/react/embed.d.ts +1 -1
  55. package/dist/embed-widget/react/embed.js +1 -1
  56. package/dist/react/artifact-types.d.ts +9 -15
  57. package/dist/react/components/QuestionForm.d.ts +2 -1
  58. package/dist/react/components/QuestionForm.js +18 -12
  59. package/dist/react/components/QuestionsPanel.d.ts +3 -2
  60. package/dist/react/components/QuestionsPanel.js +2 -2
  61. package/dist/react/features/chat-pane/agent-tools.d.ts +1 -1
  62. package/dist/react/features/chat-pane/agent-tools.js +0 -1
  63. package/dist/react/features/chat-pane/components/AgentRuntimePicker.js +5 -2
  64. package/dist/react/features/chat-pane/components/ChatPane.d.ts +1 -1
  65. package/dist/react/features/chat-pane/components/ChatPane.js +5 -2
  66. package/dist/react/features/chat-pane/create-typed-answer-poster.d.ts +2 -1
  67. package/dist/react/features/chat-pane/create-typed-answer-poster.js +33 -1
  68. package/dist/react/features/chat-pane/hooks/useAgentRuntimePicker.hooks.js +3 -2
  69. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.d.ts +4 -1
  70. package/dist/react/features/chat-pane/hooks/useChatPane.hooks.js +29 -17
  71. package/dist/react/features/chat-pane/model-options.d.ts +10 -0
  72. package/dist/react/features/chat-pane/model-options.js +51 -0
  73. package/dist/react/features/chat-pane/rules.d.ts +3 -2
  74. package/dist/react/features/chat-pane/rules.js +12 -4
  75. package/dist/react/features/chat-pane/types.d.ts +25 -1
  76. package/dist/react/hooks/assistant-chats-contracts.d.ts +164 -0
  77. package/dist/react/hooks/assistant-chats-contracts.js +2 -0
  78. package/dist/react/hooks/assistant-chats.d.ts +7 -0
  79. package/dist/react/hooks/assistant-chats.js +960 -0
  80. package/dist/react/hooks/attachment-preview-cache.d.ts +1 -1
  81. package/dist/react/hooks/attachment-preview-cache.js +1 -1
  82. package/dist/react/hooks/composer-draft-cache.d.ts +2 -2
  83. package/dist/react/hooks/composer-draft-cache.js +2 -2
  84. package/dist/react/hooks/fab-position.d.ts +90 -0
  85. package/dist/react/hooks/fab-position.js +271 -0
  86. package/dist/react/hooks/useConversation.d.ts +4 -1
  87. package/dist/react/hooks/useConversation.js +14 -12
  88. package/dist/react/hooks/useUserTextGuard.d.ts +11 -0
  89. package/dist/react/hooks/useUserTextGuard.js +19 -0
  90. package/dist/react/index.d.ts +12 -21
  91. package/dist/react/index.js +7 -0
  92. package/dist/server/durable-runs/accept.d.ts +25 -0
  93. package/dist/server/durable-runs/accept.js +21 -0
  94. package/dist/server/durable-runs/entry.d.ts +5 -0
  95. package/dist/server/durable-runs/entry.js +4 -0
  96. package/dist/server/durable-runs/recover.d.ts +5 -0
  97. package/dist/server/durable-runs/recover.js +147 -0
  98. package/dist/server/run-finalizer/ports.d.ts +2 -16
  99. package/dist/store/legacy/sqlite/messages.d.ts +24 -18
  100. package/dist/store/legacy/sqlite/messages.js +12 -2
  101. package/dist/store/pglite/index.d.ts +2 -1
  102. package/dist/store/postgres/index.d.ts +2 -1
  103. package/dist/store/runtime/chat-expiry-sweep.d.ts +40 -0
  104. package/dist/store/runtime/chat-expiry-sweep.js +70 -0
  105. package/dist/store/runtime/chat-history-store.d.ts +20 -0
  106. package/dist/store/runtime/chat-history-store.js +50 -0
  107. package/dist/store/runtime/durable-run-store.d.ts +21 -0
  108. package/dist/store/runtime/durable-run-store.js +342 -0
  109. package/dist/store/runtime/entry.d.ts +7 -0
  110. package/dist/store/runtime/entry.js +6 -0
  111. package/dist/store/runtime/kernel.d.ts +29 -0
  112. package/dist/store/runtime/kernel.js +2 -0
  113. package/dist/store/runtime/run-guarded-chat-store.d.ts +10 -0
  114. package/dist/store/runtime/run-guarded-chat-store.js +42 -0
  115. package/dist/store/runtime/run-ledger.d.ts +15 -0
  116. package/dist/store/runtime/run-ledger.js +206 -0
  117. package/dist/store/sql/boundary.d.ts +6 -0
  118. package/dist/store/sql/boundary.js +7 -1
  119. package/dist/store/sql/store.d.ts +3 -2
  120. package/dist/store/sql/store.js +9 -2
  121. package/dist/store/sqlite/index.d.ts +3 -2
  122. package/package.json +75 -10
@@ -0,0 +1,121 @@
1
+ /** Resolved target supplied by the host; the client never derives a path from a title. */
2
+ export interface ResolvedPageTarget {
3
+ readonly slug: string;
4
+ readonly title: string;
5
+ readonly path: string;
6
+ }
7
+ export type PageAction = {
8
+ readonly type: 'navigate';
9
+ readonly target: ResolvedPageTarget;
10
+ readonly auto: boolean;
11
+ } | {
12
+ readonly type: 'scroll_to' | 'highlight';
13
+ readonly target: ResolvedPageTarget;
14
+ };
15
+ export type NavigateAction = Extract<PageAction, {
16
+ type: 'navigate';
17
+ }>;
18
+ export type NonNavigateAction = Exclude<PageAction, {
19
+ type: 'navigate';
20
+ }>;
21
+ export interface DirectiveCarryingEvent {
22
+ readonly kind: string;
23
+ readonly name?: string;
24
+ readonly data?: unknown;
25
+ }
26
+ export interface SplitPageActions {
27
+ readonly navigate: NavigateAction | null;
28
+ readonly other: NonNavigateAction | null;
29
+ }
30
+ /** Narrows an untrusted, bare queued action. URL authorization remains a separate host policy. O(1). */
31
+ export declare function isQueuedPageAction(args: {
32
+ value: unknown;
33
+ }): args is {
34
+ value: PageAction;
35
+ };
36
+ /** Narrows an untrusted directive envelope, dropping unknown kinds. O(1). */
37
+ export declare function isPageActionDirective(args: {
38
+ value: unknown;
39
+ }): args is {
40
+ value: {
41
+ kind: 'page_action';
42
+ action: PageAction;
43
+ };
44
+ };
45
+ /** Preserves valid action order while ignoring unrelated events. O(events) time and output space. */
46
+ export declare function extractPageActions({ events, directiveEventName }: {
47
+ events: readonly DirectiveCarryingEvent[] | undefined;
48
+ directiveEventName: string;
49
+ }): PageAction[];
50
+ /** Returns only the first navigation and first page treatment. O(actions), O(1) extra space. */
51
+ export declare function splitPageActions({ actions }: {
52
+ actions: readonly PageAction[];
53
+ }): SplitPageActions;
54
+ export interface TargetSelectionPolicy {
55
+ readonly candidateSelector: string;
56
+ readonly excludedAncestorSelector: string;
57
+ }
58
+ /** Exact title match first, case-insensitive fallback; never searches excluded UI. O(candidates). */
59
+ export declare function findTargetElement({ title, root, selection }: {
60
+ title: string;
61
+ root: Pick<ParentNode, 'querySelectorAll'>;
62
+ selection: TargetSelectionPolicy;
63
+ }): Element | null;
64
+ export interface PageActionTimerPort {
65
+ schedule(args: {
66
+ callback: () => void;
67
+ delayMs: number;
68
+ }): unknown;
69
+ cancel(args: {
70
+ handle: unknown;
71
+ }): void;
72
+ }
73
+ export interface PageActionPort {
74
+ allows(args: {
75
+ action: PageAction;
76
+ }): boolean;
77
+ navigate(args: {
78
+ action: NavigateAction;
79
+ }): boolean;
80
+ execute(args: {
81
+ action: NonNavigateAction;
82
+ }): boolean;
83
+ clearHighlight(args: Record<string, never>): void;
84
+ }
85
+ export interface BrowserPageActionController extends PageActionPort {
86
+ applyHighlight(args: {
87
+ element: Element;
88
+ }): void;
89
+ scrollToElement(args: {
90
+ element: Element;
91
+ }): void;
92
+ }
93
+ export interface BrowserPageActionsArgs {
94
+ readonly root: Pick<ParentNode, 'querySelectorAll'>;
95
+ readonly selection: TargetSelectionPolicy;
96
+ readonly allowPath: (args: {
97
+ path: string;
98
+ }) => boolean;
99
+ readonly navigate: (args: {
100
+ path: string;
101
+ }) => void;
102
+ readonly reducedMotion: (args: Record<string, never>) => boolean;
103
+ readonly scroll: (args: {
104
+ element: Element;
105
+ options: ScrollIntoViewOptions;
106
+ }) => void;
107
+ readonly timers: PageActionTimerPort;
108
+ readonly highlightClass: string;
109
+ readonly fadeAnimationName: string;
110
+ readonly cleanupFallbackMs: number;
111
+ }
112
+ /**
113
+ * Creates page effects with injected DOM, navigation, URL policy, scroll and timers.
114
+ * At most one highlight belongs to this instance; cleanup and navigation remove it.
115
+ * URL policy is checked before queueing, navigation or page effects. Stale timers and descendant
116
+ * animation events cannot remove the current highlight. Navigation paths are resolved by the host,
117
+ * never guessed from a title; each controller owns its mutable highlight state.
118
+ * @complexity Execute O(candidates); other operations O(1), with one timer/listener retained.
119
+ */
120
+ export declare function createBrowserPageActions(args: BrowserPageActionsArgs): BrowserPageActionController;
121
+ //# sourceMappingURL=page-actions.d.ts.map
@@ -0,0 +1,113 @@
1
+ function record(value) {
2
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
3
+ }
4
+ function target(value) {
5
+ return record(value) && typeof value.slug === 'string' && typeof value.title === 'string' && typeof value.path === 'string';
6
+ }
7
+ /** Narrows an untrusted, bare queued action. URL authorization remains a separate host policy. O(1). */
8
+ export function isQueuedPageAction(args) {
9
+ const { value } = args;
10
+ if (!record(value) || !target(value.target))
11
+ return false;
12
+ return value.type === 'navigate' ? typeof value.auto === 'boolean' : value.type === 'scroll_to' || value.type === 'highlight';
13
+ }
14
+ /** Narrows an untrusted directive envelope, dropping unknown kinds. O(1). */
15
+ export function isPageActionDirective(args) {
16
+ const { value } = args;
17
+ return record(value) && value.kind === 'page_action' && isQueuedPageAction({ value: value.action });
18
+ }
19
+ /** Preserves valid action order while ignoring unrelated events. O(events) time and output space. */
20
+ export function extractPageActions({ events, directiveEventName }) {
21
+ const actions = [];
22
+ for (const event of events ?? []) {
23
+ const envelope = { value: event.data };
24
+ if (event.kind === 'ext' && event.name === directiveEventName && isPageActionDirective(envelope)) {
25
+ actions.push(envelope.value.action);
26
+ }
27
+ }
28
+ return actions;
29
+ }
30
+ /** Returns only the first navigation and first page treatment. O(actions), O(1) extra space. */
31
+ export function splitPageActions({ actions }) {
32
+ return {
33
+ navigate: actions.find((a) => a.type === 'navigate') ?? null,
34
+ other: actions.find((a) => a.type !== 'navigate') ?? null,
35
+ };
36
+ }
37
+ /** Exact title match first, case-insensitive fallback; never searches excluded UI. O(candidates). */
38
+ export function findTargetElement({ title, root, selection }) {
39
+ const needle = title.trim();
40
+ if (!needle)
41
+ return null;
42
+ const candidates = Array.from(root.querySelectorAll(selection.candidateSelector))
43
+ .filter(el => !el.closest(selection.excludedAncestorSelector));
44
+ return candidates.find(el => (el.textContent ?? '').trim() === needle)
45
+ ?? candidates.find(el => (el.textContent ?? '').trim().toLowerCase() === needle.toLowerCase()) ?? null;
46
+ }
47
+ /**
48
+ * Creates page effects with injected DOM, navigation, URL policy, scroll and timers.
49
+ * At most one highlight belongs to this instance; cleanup and navigation remove it.
50
+ * URL policy is checked before queueing, navigation or page effects. Stale timers and descendant
51
+ * animation events cannot remove the current highlight. Navigation paths are resolved by the host,
52
+ * never guessed from a title; each controller owns its mutable highlight state.
53
+ * @complexity Execute O(candidates); other operations O(1), with one timer/listener retained.
54
+ */
55
+ export function createBrowserPageActions(args) {
56
+ if (!Number.isFinite(args.cleanupFallbackMs) || args.cleanupFallbackMs < 0)
57
+ throw new Error('Invalid highlight deadline');
58
+ let current = null;
59
+ const clearHighlight = (_args) => {
60
+ if (!current)
61
+ return;
62
+ const old = current;
63
+ current = null;
64
+ args.timers.cancel({ handle: old.handle });
65
+ old.element.removeEventListener('animationend', old.onEnd);
66
+ old.element.classList.remove(args.highlightClass);
67
+ };
68
+ function highlight(element) {
69
+ if (current?.element === element)
70
+ return;
71
+ clearHighlight({});
72
+ const onEnd = event => {
73
+ if (event.target === element && event.animationName === args.fadeAnimationName)
74
+ clearHighlight({});
75
+ };
76
+ element.classList.add(args.highlightClass);
77
+ element.addEventListener('animationend', onEnd);
78
+ // Identity check makes even a late callback from a canceled timer harmless.
79
+ const handle = args.timers.schedule({ delayMs: args.cleanupFallbackMs, callback: () => {
80
+ if (current?.element === element && current.onEnd === onEnd)
81
+ clearHighlight({});
82
+ } });
83
+ current = { element, handle, onEnd };
84
+ }
85
+ const scrollToElement = ({ element }) => {
86
+ args.scroll({ element, options: { behavior: args.reducedMotion({}) ? 'auto' : 'smooth', block: 'center' } });
87
+ };
88
+ return {
89
+ applyHighlight({ element }) { highlight(element); },
90
+ scrollToElement,
91
+ allows({ action }) { return args.allowPath({ path: action.target.path }); },
92
+ navigate({ action }) {
93
+ if (!args.allowPath({ path: action.target.path }))
94
+ return false;
95
+ clearHighlight({});
96
+ args.navigate({ path: action.target.path });
97
+ return true;
98
+ },
99
+ execute({ action }) {
100
+ if (!args.allowPath({ path: action.target.path }))
101
+ return false;
102
+ const element = findTargetElement({ title: action.target.title, root: args.root, selection: args.selection });
103
+ if (!element)
104
+ return false;
105
+ scrollToElement({ element });
106
+ if (action.type === 'highlight')
107
+ highlight(element);
108
+ return true;
109
+ },
110
+ clearHighlight,
111
+ };
112
+ }
113
+ //# sourceMappingURL=page-actions.js.map
@@ -0,0 +1,18 @@
1
+ import type { AgentEvent } from "../index.js";
2
+ import type { AcceptedRunRequest, DurableRun } from "./ports.js";
3
+ export declare const CONTINUATION_DIVIDER = "\n\n---\n\nContinued\n\n";
4
+ export declare const UNKNOWN_MUTATION_ERROR = "This operation may already be done. Its earlier outcome is unknown; verify the current state before repeating it.";
5
+ /** Object-key order and a new tool-use id must not turn the same mutation into a new operation. */
6
+ export declare function canonicalToolInput({ value }: {
7
+ value: unknown;
8
+ }, _optional?: {}): string;
9
+ export declare function hasUnknownToolCall({ events, toolId, input }: {
10
+ events: readonly AgentEvent[];
11
+ toolId: string;
12
+ input: unknown;
13
+ }, _optional?: {}): boolean;
14
+ export declare function continuationRequest({ run, native }: {
15
+ run: DurableRun;
16
+ native: boolean;
17
+ }, _optional?: {}): AcceptedRunRequest;
18
+ //# sourceMappingURL=continuation.d.ts.map
@@ -0,0 +1,61 @@
1
+ export const CONTINUATION_DIVIDER = "\n\n---\n\nContinued\n\n";
2
+ export const UNKNOWN_MUTATION_ERROR = "This operation may already be done. Its earlier outcome is unknown; verify the current state before repeating it.";
3
+ /** Object-key order and a new tool-use id must not turn the same mutation into a new operation. */
4
+ export function canonicalToolInput({ value }, _optional = {}) {
5
+ if (Array.isArray(value))
6
+ return `[${value.map((item) => canonicalToolInput({ value: item }, {})).join(",")}]`;
7
+ if (value !== null && typeof value === "object") {
8
+ const object = value;
9
+ return `{${Object.keys(object).sort().map((key) => `${JSON.stringify(key)}:${canonicalToolInput({ value: object[key] }, {})}`).join(",")}}`;
10
+ }
11
+ return JSON.stringify(value) ?? "null";
12
+ }
13
+ function delegatedCall(event) {
14
+ const input = event.input;
15
+ if (input && typeof input.toolId === "string" && /execute.*delegated_tool/.test(event.name))
16
+ return { toolId: input.toolId, input: input.input };
17
+ return { toolId: event.name, input: event.input };
18
+ }
19
+ export function hasUnknownToolCall({ events, toolId, input }, _optional = {}) {
20
+ const completed = new Set(events.filter((event) => event.kind === "tool_result").map((event) => event.toolUseId));
21
+ const canonical = canonicalToolInput({ value: input }, {});
22
+ return events.some((event) => {
23
+ if (event.kind !== "tool_use" || completed.has(event.id))
24
+ return false;
25
+ const call = delegatedCall(event);
26
+ return call.toolId === toolId && canonicalToolInput({ value: call.input }, {}) === canonical;
27
+ });
28
+ }
29
+ function toolHistory(events) {
30
+ const results = new Map(events.filter((event) => event.kind === "tool_result").map((event) => [event.toolUseId, event]));
31
+ return events.filter((event) => event.kind === "tool_use").map((event) => {
32
+ const result = results.get(event.id);
33
+ return JSON.stringify({ call: delegatedCall(event), result: result ?? "OUTCOME UNKNOWN: verify state before repeating" });
34
+ }).join("\n");
35
+ }
36
+ export function continuationRequest({ run, native }, _optional = {}) {
37
+ const original = JSON.parse(run.request.contextRef);
38
+ // Native sessions may have disappeared on deploy. Reconstruction includes the saved answer
39
+ // and tool results for every runtime; completed side effects must never be silently repeated.
40
+ const transcript = run.transcript.map((message) => `${message.role}: ${message.content}`).join("\n\n");
41
+ const prompt = [
42
+ "Continue the interrupted answer from the saved work below. Preserve completed work. Do not repeat completed mutations.",
43
+ "Calls without a result have an unknown outcome: inspect current state before any retry. Expired plans or approvals must be re-planned and re-approved; never reuse them.",
44
+ "A pending question may be asked again when its process was lost. The interrupted segment remains visible; append the continuation.",
45
+ `Accepted task:\n${String(original.prompt ?? "")}`,
46
+ native ? "Native session is confirmed; continue its saved task." : `Saved conversation:\n${transcript}`,
47
+ `Saved partial answer:\n${run.message.content}`,
48
+ `Saved tool calls and results:\n${toolHistory(run.message.events ?? [])}`,
49
+ ].join("\n\n");
50
+ return {
51
+ ...run.request,
52
+ contextRef: JSON.stringify({ ...original, prompt, conversationId: run.conversationId,
53
+ assistantMessageId: run.messageId, principalId: run.principalId,
54
+ recoveryMode: native ? "native" : "reconstruction", recoverySessionId: native ? run.sessionId : undefined,
55
+ // Old attachment claims have been consumed. Saved transcript/tool results carry their
56
+ // observations; attempting to claim the same ids again would fail before execution.
57
+ attachmentIds: [],
58
+ }),
59
+ };
60
+ }
61
+ //# sourceMappingURL=continuation.js.map
@@ -0,0 +1,53 @@
1
+ import type { ChatMessage } from "../index.js";
2
+ import type { DurableRunStore } from "./ports.js";
3
+ import type { RunRef, RunProgress, RunSettlement } from "./run-contracts.js";
4
+ export type { RunRef, RunProgress, RunSettlement } from "./run-contracts.js";
5
+ /** {@link ChatRunLedger.unlessSettled}'s result: the write's value, or that the run had settled. */
6
+ export type UnlessSettled<T> = {
7
+ readonly written: true;
8
+ readonly value: T;
9
+ } | {
10
+ readonly written: false;
11
+ };
12
+ /** Internal boot recovery, never an HTTP history read. The owner comes from storage, not a client. */
13
+ export interface InterruptedChatTurn {
14
+ readonly principalId: string | null;
15
+ readonly conversationId: string;
16
+ readonly message: ChatMessage;
17
+ }
18
+ export interface ChatRunRecoveryOptions {
19
+ /** True retains the row: a daemon run was adopted with bounded resolution of uncertainty.
20
+ * Called outside storage transactions so a daemon request cannot hold the database lock. */
21
+ readonly recover?: (input: InterruptedChatTurn) => Promise<boolean>;
22
+ }
23
+ export interface ChatRunLedger {
24
+ readonly durable?: DurableRunStore;
25
+ /**
26
+ * Runs `write` only while `run`'s row holds no terminal status (an unknown row, another run id, or
27
+ * a row still `queued`/`running` all count as unsettled), with no settle able to land in between.
28
+ * `write` must reach the chat database through this ledger's kernel (the same connection, on
29
+ * SQLite) so it joins the transaction.
30
+ */
31
+ unlessSettled<T>(required: {
32
+ run: RunRef;
33
+ write: () => Promise<T>;
34
+ }, optional?: {}): Promise<UnlessSettled<T>>;
35
+ /**
36
+ * Writes the run's final content, events and status — only if the row still belongs to that run
37
+ * and is not yet terminal. Resolves `true` when this call was the one that settled it.
38
+ */
39
+ settle(settlement: RunSettlement, optional?: {}): Promise<boolean>;
40
+ /**
41
+ * Saves what a still-running run has produced so far (content and events; the status stays), so
42
+ * a process that dies mid-run leaves its partial answer for {@link reconcileInterrupted} to keep.
43
+ * A no-op once the row is terminal or belongs to another run. Resolves `true` when it wrote.
44
+ */
45
+ checkpoint(progress: RunProgress, optional?: {}): Promise<boolean>;
46
+ /** Serving-only discovery of unfinished rows. The injected recovery coordinator owns adopted
47
+ * runs; rows it cannot adopt are canceled, preserving saved work. Returns handled rows.
48
+ * Export apps must not invoke discovery while a serving daemon is still alive. */
49
+ reconcileInterrupted(required?: {
50
+ now?: number;
51
+ }, optional?: ChatRunRecoveryOptions): Promise<number>;
52
+ }
53
+ //# sourceMappingURL=ledger-contracts.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ledger-contracts.js.map
@@ -0,0 +1,6 @@
1
+ export declare const RECOVERY_LIMIT = 3;
2
+ export declare const RECOVERY_WINDOW_MS: number;
3
+ export declare const RUN_STALENESS_MS: number;
4
+ export declare const SAVED_WORK_NOTICE = "The assistant stopped after making no progress. Saved work is above.";
5
+ export declare const EXHAUSTED_NOTICE = "The assistant could not continue this answer. Saved work is above.";
6
+ //# sourceMappingURL=policies.d.ts.map
@@ -0,0 +1,6 @@
1
+ export const RECOVERY_LIMIT = 3;
2
+ export const RECOVERY_WINDOW_MS = 5 * 60_000;
3
+ export const RUN_STALENESS_MS = 30 * 60_000;
4
+ export const SAVED_WORK_NOTICE = "The assistant stopped after making no progress. Saved work is above.";
5
+ export const EXHAUSTED_NOTICE = "The assistant could not continue this answer. Saved work is above.";
6
+ //# sourceMappingURL=policies.js.map
@@ -0,0 +1,123 @@
1
+ import type { AgentEvent, ChatMessage } from "../index.js";
2
+ import type { RunRef, RunSettlement } from "./ledger-contracts.js";
3
+ export interface AcceptedRunRequest {
4
+ readonly agentId?: string;
5
+ readonly contextRef: string;
6
+ }
7
+ export interface DurableRun extends RunRef {
8
+ readonly workspaceId: string;
9
+ readonly principalId: string | null;
10
+ readonly engine: string;
11
+ readonly message: ChatMessage;
12
+ readonly request: AcceptedRunRequest;
13
+ readonly transcript: readonly ChatMessage[];
14
+ readonly recoveryCount: number;
15
+ readonly recoveryDeadline: number | null;
16
+ readonly attemptStartedAt: number;
17
+ readonly lastProgressAt: number;
18
+ readonly cancelReason: string | null;
19
+ readonly sessionId: string | null;
20
+ readonly sessionConfirmed: boolean;
21
+ readonly child: {
22
+ pid: number;
23
+ startedAt: string;
24
+ } | null;
25
+ readonly attemptBase: readonly AgentEvent[];
26
+ }
27
+ export interface DurableRunStore {
28
+ load(required: {
29
+ messageId: string;
30
+ }, optional: {}): Promise<DurableRun | null>;
31
+ find(required: {
32
+ runId: string;
33
+ principalId?: string;
34
+ }, optional: {}): Promise<DurableRun | null>;
35
+ accept(required: RunRef & {
36
+ principalId: string;
37
+ workspaceId: string;
38
+ request: AcceptedRunRequest;
39
+ now: number;
40
+ }, optional: {}): Promise<DurableRun | null>;
41
+ advance(required: {
42
+ run: DurableRun;
43
+ nextRunId: string;
44
+ now: number;
45
+ events: readonly AgentEvent[];
46
+ }, optional: {}): Promise<boolean>;
47
+ cancel(required: {
48
+ runId: string;
49
+ reason: string;
50
+ }, optional: {}): Promise<void>;
51
+ cancelPending(required: RunRef & {
52
+ principalId: string;
53
+ workspaceId: string;
54
+ now: number;
55
+ }, optional: {}): Promise<DurableRun | null>;
56
+ captureSession(required: {
57
+ runId: string;
58
+ sessionId: string;
59
+ confirmed?: boolean;
60
+ child?: {
61
+ pid: number;
62
+ startedAt: string;
63
+ };
64
+ }, optional: {}): Promise<boolean>;
65
+ clearSession(required: {
66
+ runId: string;
67
+ sessionId: string;
68
+ }, optional: {}): Promise<void>;
69
+ recoveryClock(required: {
70
+ runId: string;
71
+ now: number;
72
+ active: boolean;
73
+ }, optional: {}): Promise<void>;
74
+ guardTool(required: {
75
+ runId: string;
76
+ toolId: string;
77
+ input: unknown;
78
+ toolUseId?: string;
79
+ }, optional: {}): Promise<"allowed" | "unknown" | "stale">;
80
+ completeTool(required: {
81
+ runId: string;
82
+ toolUseId: string;
83
+ content: string;
84
+ }, optional: {}): Promise<void>;
85
+ }
86
+ export type RunProbe = "live" | "dead" | "uncertain";
87
+ export interface RecoveryPorts {
88
+ readonly store: DurableRunStore;
89
+ readonly now: () => number;
90
+ readonly mintRunId: () => string;
91
+ readonly probe: (required: DurableRun, optional: {}) => Promise<RunProbe>;
92
+ readonly attach: (required: DurableRun, optional: {}) => void;
93
+ readonly launch: (required: {
94
+ run: DurableRun;
95
+ request: AcceptedRunRequest;
96
+ }, optional: {}) => Promise<void>;
97
+ readonly cancelAttempt: (required: DurableRun, optional: {}) => Promise<void>;
98
+ readonly verifyChildDead: (required: {
99
+ pid: number;
100
+ startedAt: string;
101
+ }, optional: {}) => Promise<boolean>;
102
+ readonly supportsNativeResume: (required: {
103
+ agentId: string;
104
+ }, optional: {}) => boolean;
105
+ readonly settle: (required: RunSettlement, optional: {}) => Promise<boolean>;
106
+ }
107
+ export type RecoveryTrigger = "boot" | "browser" | "stream" | "timeout" | "attempt-failed";
108
+ export type RecoveryResult = "gone" | "terminal" | "reattached" | "waiting" | "continued" | "superseded" | "finalized";
109
+ export interface DurableRecovery {
110
+ recover(required: {
111
+ messageId: string;
112
+ trigger: RecoveryTrigger;
113
+ expectedRunId?: string;
114
+ }, optional: {
115
+ /** Fresh HTTP 200 from the owning watch's bounded probe, scoped to exactly that attempt. */
116
+ liveRunId?: string;
117
+ }): Promise<RecoveryResult>;
118
+ }
119
+ /** Host run-ID classification; never infer transport identity from an engine default. */
120
+ export type RunIdPolicy = (required: {
121
+ runId: string;
122
+ }, optional?: {}) => boolean;
123
+ //# sourceMappingURL=ports.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ports.js.map
@@ -0,0 +1,17 @@
1
+ import type { AgentEvent } from "../events.js";
2
+ import type { ChatRunStatus } from "../messages.js";
3
+ /** Exactly which run owns the row; the host binds authorization and storage scope. */
4
+ export interface RunRef {
5
+ readonly conversationId: string;
6
+ readonly messageId: string;
7
+ readonly runId: string;
8
+ }
9
+ export interface RunProgress extends RunRef {
10
+ readonly content: string;
11
+ readonly events: readonly AgentEvent[];
12
+ }
13
+ export interface RunSettlement extends RunProgress {
14
+ readonly status: Extract<ChatRunStatus, 'succeeded' | 'failed' | 'canceled'>;
15
+ readonly endedAt: number;
16
+ }
17
+ //# sourceMappingURL=run-contracts.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=run-contracts.js.map
@@ -0,0 +1,4 @@
1
+ /** Every checkpoint, settlement and attempt CAS shares this message lock. */
2
+ import type { RunRef } from "./ledger-contracts.js";
3
+ export declare function runLockKey(run: RunRef, _optional?: {}): string;
4
+ //# sourceMappingURL=run-lock.d.ts.map
@@ -0,0 +1,4 @@
1
+ export function runLockKey(run, _optional = {}) {
2
+ return `chat-run:${run.conversationId}:${run.messageId}`;
3
+ }
4
+ //# sourceMappingURL=run-lock.js.map
@@ -1,16 +1,7 @@
1
1
  /**
2
- * `@jini-ai/chat/core` — framework-free chat vocabulary + pure parsers.
3
- *
4
- * Zero React, zero DOM/browser globals, zero Node built-ins, zero imports
5
- * from any product package's scope. See ADS-memory/reports/jini-port/extraction-plan.md
6
- * §12 C2/C3 and ADS-memory/reports/jini-port/recon/r4b-webui-design.md §1 for the design
7
- * this package targets, and archived provenance ledger for exact provenance.
8
- *
9
- * Was `@jini-ai/chat-core` (its own top-level package, one entry point) through npm 0.1.2 —
10
- * consolidated 2026-08-03 into `@jini-ai/chat`'s `./core` subpath alongside `./react` (formerly
11
- * `@jini-ai/ui`'s `./chat` export), following `@jini-ai/admin`'s umbrella-with-subpaths precedent.
12
- * `@jini-ai/chat-core` is retired, not deprecated-and-kept — its published npm versions
13
- * (0.1.0–0.1.2) go end-of-life as-is; `@jini-ai/chat` supersedes it.
2
+ * @jini-ai/chat/core — framework-free chat vocabulary and pure parsers.
3
+ * No React, DOM/browser globals, Node built-ins or product-package imports (extraction-plan §12
4
+ * C2/C3). Hosts can implement the transport port without importing a browser component graph.
14
5
  */
15
6
  export * from './events.js';
16
7
  export * from './messages.js';
@@ -27,13 +18,8 @@ export * from './run-activity.js';
27
18
  export * from './surface-expiry.js';
28
19
  export * from './assistant-content.js';
29
20
  export * from './compact-events.js';
30
- /**
31
- * The chat pane's own capability manifest (`CHAT_CAPABILITIES`). The framework-free agent-control
32
- * vocabulary this used to sit alongside — `CapabilityDef`, `PAGE_CAPABILITIES`, the
33
- * `data-agent-*` convention, the policy gate, the protocol projections — moved to `@jini-ai/agentic`
34
- * on 2026-07-26; import it directly rather than through this package. See
35
- * `packages/agentic/archived provenance ledger` and this package's own archived provenance ledger for the extraction.
36
- */
21
+ /** Chat-pane capability manifest. Generic agent-control vocabulary, markup and projections are
22
+ * owned by @jini-ai/agentic; import them there so non-chat callers stay independent of chat. */
37
23
  export * from './agentic/index.js';
38
24
  /**
39
25
  * Durable chat history — the storage-neutral `ChatHistoryStore` port and the local title
@@ -45,4 +31,5 @@ export * from './persistence/index.js';
45
31
  export { recoveredRunEvents } from './durable-projection.js';
46
32
  export { composerHistoryKeyAction, createComposerHistoryState, mergeComposerHistory, normalizeComposerHistory, transitionComposerHistory, } from './composer-history.js';
47
33
  export type { ComposerHistoryKeyInput, ComposerHistoryState, ComposerHistoryStoragePort } from './composer-history.js';
34
+ export * from './user-text-redaction.js';
48
35
  //# sourceMappingURL=index.d.ts.map
@@ -1,16 +1,7 @@
1
1
  /**
2
- * `@jini-ai/chat/core` — framework-free chat vocabulary + pure parsers.
3
- *
4
- * Zero React, zero DOM/browser globals, zero Node built-ins, zero imports
5
- * from any product package's scope. See ADS-memory/reports/jini-port/extraction-plan.md
6
- * §12 C2/C3 and ADS-memory/reports/jini-port/recon/r4b-webui-design.md §1 for the design
7
- * this package targets, and archived provenance ledger for exact provenance.
8
- *
9
- * Was `@jini-ai/chat-core` (its own top-level package, one entry point) through npm 0.1.2 —
10
- * consolidated 2026-08-03 into `@jini-ai/chat`'s `./core` subpath alongside `./react` (formerly
11
- * `@jini-ai/ui`'s `./chat` export), following `@jini-ai/admin`'s umbrella-with-subpaths precedent.
12
- * `@jini-ai/chat-core` is retired, not deprecated-and-kept — its published npm versions
13
- * (0.1.0–0.1.2) go end-of-life as-is; `@jini-ai/chat` supersedes it.
2
+ * @jini-ai/chat/core — framework-free chat vocabulary and pure parsers.
3
+ * No React, DOM/browser globals, Node built-ins or product-package imports (extraction-plan §12
4
+ * C2/C3). Hosts can implement the transport port without importing a browser component graph.
14
5
  */
15
6
  export * from './events.js';
16
7
  export * from './messages.js';
@@ -27,13 +18,8 @@ export * from './run-activity.js';
27
18
  export * from './surface-expiry.js';
28
19
  export * from './assistant-content.js';
29
20
  export * from './compact-events.js';
30
- /**
31
- * The chat pane's own capability manifest (`CHAT_CAPABILITIES`). The framework-free agent-control
32
- * vocabulary this used to sit alongside — `CapabilityDef`, `PAGE_CAPABILITIES`, the
33
- * `data-agent-*` convention, the policy gate, the protocol projections — moved to `@jini-ai/agentic`
34
- * on 2026-07-26; import it directly rather than through this package. See
35
- * `packages/agentic/archived provenance ledger` and this package's own archived provenance ledger for the extraction.
36
- */
21
+ /** Chat-pane capability manifest. Generic agent-control vocabulary, markup and projections are
22
+ * owned by @jini-ai/agentic; import them there so non-chat callers stay independent of chat. */
37
23
  export * from './agentic/index.js';
38
24
  /**
39
25
  * Durable chat history — the storage-neutral `ChatHistoryStore` port and the local title
@@ -44,4 +30,5 @@ export * from './agentic/index.js';
44
30
  export * from './persistence/index.js';
45
31
  export { recoveredRunEvents } from './durable-projection.js';
46
32
  export { composerHistoryKeyAction, createComposerHistoryState, mergeComposerHistory, normalizeComposerHistory, transitionComposerHistory, } from './composer-history.js';
33
+ export * from './user-text-redaction.js';
47
34
  //# sourceMappingURL=index.js.map
@@ -33,6 +33,8 @@ export interface ChatAttachment {
33
33
  * host layers those on top via its own message extension, not this type.
34
34
  */
35
35
  export interface ChatMessage {
36
+ /** Transient safe receipt; storage requires no new column. */
37
+ secretRedaction?: import('./user-text-redaction.js').SecretRedactionSignal;
36
38
  id: string;
37
39
  role: ChatRole;
38
40
  content: string;
@@ -1,12 +1,3 @@
1
- /**
2
- * @module question-form/format
3
- *
4
- * Answer presentation: turning a submitted answer set into the prose the
5
- * agent reads on its next turn, plus the label/value lookups also used by
6
- * `normalize.ts`'s default-value resolution. Split out of the original
7
- * single-file `question-form.ts`; see `../question-form.ts` for the public
8
- * facade.
9
- */
10
1
  import type { FormOption, QuestionForm } from './types.js';
11
2
  /**
12
3
  * Format a finished set of answers into a prose user message the agent can