@agent-api/app-engine 0.0.8 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -44,7 +44,8 @@ Host applications should call `configureAgentAppRuntime()` during startup so con
44
44
  - `@agent-api/app-engine/core`: UI-neutral APIs for auth, config, profiles, conversations, updates, local workdir setup, and agent turns.
45
45
  - `@agent-api/app-engine/workbench`: optional app/workbench state controllers for apps that want Agent API's conversation workflow.
46
46
  - `@agent-api/app-engine/terminal`: optional terminal-facing helpers for transcript wrapping, input viewport rendering, and spinner glyphs.
47
- - `@agent-api/app-engine`: compatibility barrel that re-exports the layers above.
47
+
48
+ The root `@agent-api/app-engine` entry is intentionally empty. Use an explicit subpath so your application depends on a clear API layer.
48
49
 
49
50
  ## Boundaries
50
51
 
package/dist/index.d.ts CHANGED
@@ -1 +1 @@
1
- export * from "./engine/index.js";
1
+ export {};
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export * from "./engine/index.js";
1
+ export {};
@@ -2,5 +2,6 @@ export type { WorkbenchInputController } from "./workbench/input-controller.js";
2
2
  export { createWorkbenchInputController } from "./workbench/input-controller.js";
3
3
  export type { WorkbenchRenderModel } from "./workbench/render-model.js";
4
4
  export { buildWorkbenchRenderModel, busySpinner, pendingLocalLabel, } from "./workbench/render-model.js";
5
+ export type { TranscriptLine, TranscriptSpan, TranscriptViewModel, } from "./workbench/view-model.js";
5
6
  export { buildTranscriptLines, buildTranscriptViewModel, elapsedDots, spinnerGlyph, } from "./workbench/view-model.js";
6
7
  export { activityColor } from "./workbench/state.js";
@@ -5,6 +5,13 @@ export type TranscriptLine = {
5
5
  color?: string;
6
6
  bold?: boolean;
7
7
  inverse?: boolean;
8
+ spans?: TranscriptSpan[];
9
+ };
10
+ export type TranscriptSpan = {
11
+ text: string;
12
+ color?: string;
13
+ bold?: boolean;
14
+ inverse?: boolean;
8
15
  };
9
16
  export interface TranscriptViewModel {
10
17
  lines: TranscriptLine[];
@@ -80,16 +80,85 @@ function markdownTranscriptLine(line, options) {
80
80
  return [{ text: "─".repeat(Math.min(48, options.width)), color: "gray" }];
81
81
  const bullet = /^(\s*)[-*]\s+(.+)$/.exec(line);
82
82
  if (bullet)
83
- return wrapTranscriptText(`${bullet[1]}• ${bullet[2]}`, options.width).map((text) => ({ text }));
83
+ return wrapMarkdownInline(`${bullet[1]}• ${bullet[2]}`, options.width);
84
84
  const numbered = /^(\s*)(\d+\.)\s+(.+)$/.exec(line);
85
85
  if (numbered)
86
- return wrapTranscriptText(`${numbered[1]}${numbered[2]} ${numbered[3]}`, options.width).map((text) => ({ text }));
86
+ return wrapMarkdownInline(`${numbered[1]}${numbered[2]} ${numbered[3]}`, options.width);
87
87
  const quote = /^\s*>\s?(.+)$/.exec(line);
88
88
  if (quote)
89
- return wrapTranscriptText(`│ ${quote[1]}`, options.width).map((text) => ({ text, color: "gray" }));
90
- return wrapTranscriptText(line, options.width).map((text) => ({ text }));
89
+ return wrapMarkdownInline(`│ ${quote[1]}`, options.width).map((item) => ({ ...item, color: "gray" }));
90
+ return wrapMarkdownInline(line, options.width);
91
91
  }
92
- function wrapTranscriptText(text, width) {
92
+ function wrapMarkdownInline(text, width) {
93
+ return wrapTranscriptSpans(markdownInlineSpans(text), width);
94
+ }
95
+ function markdownInlineSpans(text) {
96
+ const spans = [];
97
+ const pattern = /(\*\*([^*]+)\*\*)|(\[([^\]]+)\]\(([^)]+)\))/g;
98
+ let lastIndex = 0;
99
+ for (const match of text.matchAll(pattern)) {
100
+ const index = match.index ?? 0;
101
+ if (index > lastIndex) {
102
+ spans.push({ text: text.slice(lastIndex, index) });
103
+ }
104
+ if (match[2]) {
105
+ spans.push({ text: match[2], bold: true });
106
+ }
107
+ else if (match[4]) {
108
+ spans.push({ text: match[4], color: "cyan" });
109
+ if (match[5])
110
+ spans.push({ text: ` (${match[5]})`, color: "gray" });
111
+ }
112
+ lastIndex = index + match[0].length;
113
+ }
114
+ if (lastIndex < text.length) {
115
+ spans.push({ text: text.slice(lastIndex) });
116
+ }
117
+ return spans.length > 0 ? spans : [{ text }];
118
+ }
119
+ function wrapTranscriptSpans(spans, width) {
120
+ const text = spans.map((span) => span.text).join("");
121
+ const wrapped = wrapTranscriptText(text, width, { trimStart: false });
122
+ const lines = [];
123
+ let offset = 0;
124
+ for (const line of wrapped) {
125
+ const lineSpans = sliceSpans(spans, offset, line.length);
126
+ offset += line.length;
127
+ while (text[offset] === " ")
128
+ offset += 1;
129
+ lines.push({
130
+ text: line,
131
+ spans: lineSpans.length > 0 ? lineSpans : undefined,
132
+ });
133
+ }
134
+ return lines;
135
+ }
136
+ function sliceSpans(spans, offset, length) {
137
+ const output = [];
138
+ let position = 0;
139
+ let remaining = length;
140
+ for (const span of spans) {
141
+ if (remaining <= 0)
142
+ break;
143
+ const spanEnd = position + span.text.length;
144
+ if (spanEnd <= offset) {
145
+ position = spanEnd;
146
+ continue;
147
+ }
148
+ if (position >= offset + length)
149
+ break;
150
+ const start = Math.max(0, offset - position);
151
+ const end = Math.min(span.text.length, start + remaining);
152
+ const text = span.text.slice(start, end);
153
+ if (text) {
154
+ output.push({ ...span, text });
155
+ remaining -= text.length;
156
+ }
157
+ position = spanEnd;
158
+ }
159
+ return output;
160
+ }
161
+ function wrapTranscriptText(text, width, options = {}) {
93
162
  const max = Math.max(12, width);
94
163
  if (text.length === 0)
95
164
  return [""];
@@ -105,7 +174,7 @@ function wrapTranscriptText(text, width) {
105
174
  const chunk = useSoftBreak ? soft : hard.text;
106
175
  const index = useSoftBreak ? softBreak : hard.length;
107
176
  lines.push(chunk.trimEnd());
108
- rest = rest.slice(index).trimStart();
177
+ rest = options.trimStart === false ? rest.slice(index) : rest.slice(index).trimStart();
109
178
  }
110
179
  lines.push(rest);
111
180
  return lines;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agent-api/app-engine",
3
- "version": "0.0.8",
3
+ "version": "0.1.0",
4
4
  "description": "Renderer-neutral application engine for Agent API apps",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/scalebox-dev/agent-tui#readme",