@datalayer/agent-runtimes 1.5.32 → 1.5.34
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/dist-embed/chunks/{ChatView-C_NOLqwI.js → ChatView-kCmBbnDa.js} +3 -3
- package/dist-embed/datalayer-app-main.js +10 -7
- package/lib/apps/plugins/shield-displays/DiffCard.d.ts +17 -0
- package/lib/apps/plugins/shield-displays/DiffCard.js +34 -0
- package/lib/apps/plugins/shield-displays/TerminalCard.d.ts +16 -0
- package/lib/apps/plugins/shield-displays/TerminalCard.js +31 -0
- package/lib/apps/plugins/shield-displays/index.d.ts +9 -0
- package/lib/apps/plugins/shield-displays/index.js +63 -0
- package/lib/apps/presets.js +4 -0
- package/lib/portable/displays.d.ts +47 -0
- package/lib/portable/displays.js +170 -0
- package/lib/portable/fixtures.d.ts +9 -0
- package/lib/portable/fixtures.js +36 -0
- package/lib/portable/index.d.ts +2 -1
- package/lib/portable/index.js +4 -1
- package/lib/portable/views.d.ts +3 -1
- package/lib/specs/agents/agents.d.ts +1 -0
- package/lib/specs/agents/agents.js +49 -0
- package/package.json +1 -1
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a coding agent changed in one file, as a unified diff (SHIELD §4.3,
|
|
3
|
+
* SH-16): the path and the count in the header, each line signed and
|
|
4
|
+
* coloured, the file's language on the block for a highlighter. What it
|
|
5
|
+
* reads is `portable/displays`' (`protocolDisplayOf`, `unifiedDiffLines`),
|
|
6
|
+
* so the phone draws the same thing from the same contract.
|
|
7
|
+
*
|
|
8
|
+
* @module apps/plugins/shield-displays/DiffCard
|
|
9
|
+
*/
|
|
10
|
+
import type { JSX } from 'react';
|
|
11
|
+
import { type ProtocolDisplay } from '../../../portable';
|
|
12
|
+
export type DiffCardProps = {
|
|
13
|
+
display: Extract<ProtocolDisplay, {
|
|
14
|
+
kind: 'diff';
|
|
15
|
+
}>;
|
|
16
|
+
};
|
|
17
|
+
export declare function DiffCard({ display }: DiffCardProps): JSX.Element;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '@primer/react';
|
|
3
|
+
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { displayLine, unifiedDiffLines, } from '../../../portable';
|
|
5
|
+
const COLOURS = {
|
|
6
|
+
context: { bg: 'transparent', fg: 'fg.muted', sign: ' ' },
|
|
7
|
+
add: { bg: 'success.subtle', fg: 'success.fg', sign: '+' },
|
|
8
|
+
remove: { bg: 'danger.subtle', fg: 'danger.fg', sign: '-' },
|
|
9
|
+
};
|
|
10
|
+
export function DiffCard({ display }) {
|
|
11
|
+
const lines = unifiedDiffLines(display.oldText, display.newText);
|
|
12
|
+
return (_jsxs(Box, { "data-shield-display": "diff", "data-language": display.language, sx: {
|
|
13
|
+
width: '100%',
|
|
14
|
+
border: '1px solid',
|
|
15
|
+
borderColor: 'border.default',
|
|
16
|
+
borderRadius: 2,
|
|
17
|
+
overflow: 'hidden',
|
|
18
|
+
fontSize: 0,
|
|
19
|
+
}, children: [_jsxs(Box, { sx: {
|
|
20
|
+
px: 2,
|
|
21
|
+
py: 1,
|
|
22
|
+
bg: 'canvas.subtle',
|
|
23
|
+
borderBottom: '1px solid',
|
|
24
|
+
borderColor: 'border.default',
|
|
25
|
+
display: 'flex',
|
|
26
|
+
justifyContent: 'space-between',
|
|
27
|
+
}, children: [_jsx(Text, { sx: { fontFamily: 'mono', fontWeight: 'bold' }, children: display.path }), _jsx(Text, { sx: { color: 'fg.muted' }, children: displayLine(display) })] }), _jsx(Box, { as: "pre", className: `language-${display.language}`, sx: { m: 0, overflowX: 'auto', fontFamily: 'mono' }, children: lines.map((line, index) => (_jsxs(Box, { as: "span", "data-diff-line": line.kind, sx: {
|
|
28
|
+
display: 'block',
|
|
29
|
+
px: 2,
|
|
30
|
+
bg: COLOURS[line.kind].bg,
|
|
31
|
+
color: COLOURS[line.kind].fg,
|
|
32
|
+
whiteSpace: 'pre',
|
|
33
|
+
}, children: [COLOURS[line.kind].sign, line.text] }, index))) })] }));
|
|
34
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a coding agent ran (SHIELD §4.3, SH-16): the command in the header,
|
|
3
|
+
* the output kept after the terminal was released, and how it ended — the
|
|
4
|
+
* exit code, or *running* while it runs. What it reads is
|
|
5
|
+
* `portable/displays`' (`protocolDisplayOf`), the phone's contract too.
|
|
6
|
+
*
|
|
7
|
+
* @module apps/plugins/shield-displays/TerminalCard
|
|
8
|
+
*/
|
|
9
|
+
import type { JSX } from 'react';
|
|
10
|
+
import { type ProtocolDisplay } from '../../../portable';
|
|
11
|
+
export type TerminalCardProps = {
|
|
12
|
+
display: Extract<ProtocolDisplay, {
|
|
13
|
+
kind: 'terminal';
|
|
14
|
+
}>;
|
|
15
|
+
};
|
|
16
|
+
export declare function TerminalCard({ display }: TerminalCardProps): JSX.Element;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Text } from '@primer/react';
|
|
3
|
+
import { Box } from '@datalayer/primer-addons';
|
|
4
|
+
import { displayLine } from '../../../portable';
|
|
5
|
+
export function TerminalCard({ display }) {
|
|
6
|
+
const failed = display.exitCode !== null && display.exitCode !== 0;
|
|
7
|
+
return (_jsxs(Box, { "data-shield-display": "terminal", "data-exit-code": display.exitCode === null ? 'running' : display.exitCode, sx: {
|
|
8
|
+
width: '100%',
|
|
9
|
+
border: '1px solid',
|
|
10
|
+
borderColor: failed ? 'danger.muted' : 'border.default',
|
|
11
|
+
borderRadius: 2,
|
|
12
|
+
overflow: 'hidden',
|
|
13
|
+
fontSize: 0,
|
|
14
|
+
}, children: [_jsxs(Box, { sx: {
|
|
15
|
+
px: 2,
|
|
16
|
+
py: 1,
|
|
17
|
+
bg: 'canvas.subtle',
|
|
18
|
+
borderBottom: '1px solid',
|
|
19
|
+
borderColor: 'border.default',
|
|
20
|
+
display: 'flex',
|
|
21
|
+
justifyContent: 'space-between',
|
|
22
|
+
}, children: [_jsx(Text, { sx: { fontFamily: 'mono', fontWeight: 'bold' }, children: display.command ? `$ ${display.command}` : display.terminalId }), _jsx(Text, { sx: { color: failed ? 'danger.fg' : 'fg.muted' }, children: displayLine(display) })] }), _jsx(Box, { as: "pre", sx: {
|
|
23
|
+
m: 0,
|
|
24
|
+
px: 2,
|
|
25
|
+
py: 1,
|
|
26
|
+
overflowX: 'auto',
|
|
27
|
+
fontFamily: 'mono',
|
|
28
|
+
whiteSpace: 'pre-wrap',
|
|
29
|
+
bg: 'canvas.default',
|
|
30
|
+
}, children: display.output || (display.exitCode === null ? '…' : '(no output)') })] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { ReactorPlugin } from '@datalayer/reactor';
|
|
2
|
+
export declare const SHIELD_DISPLAYS_PLUGIN_NAME = "@datalayer/loop-plugin-shield-displays";
|
|
3
|
+
/** The plugin: two renderers, no tool binding. */
|
|
4
|
+
export declare function createShieldDisplaysPlugin(): ReactorPlugin<Record<string, never>, unknown, unknown>;
|
|
5
|
+
/** The instance the preset mounts. */
|
|
6
|
+
export declare const ShieldDisplaysPlugin: ReactorPlugin<Record<string, never>, unknown, unknown>;
|
|
7
|
+
export { DiffCard, type DiffCardProps } from './DiffCard';
|
|
8
|
+
export { TerminalCard, type TerminalCardProps } from './TerminalCard';
|
|
9
|
+
export default ShieldDisplaysPlugin;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025-2026 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* `@datalayer/loop-plugin-shield-displays` — a coding agent's *diff* and
|
|
7
|
+
* *terminal*, drawn where its tool results carry them (SHIELD §4.3, SH-16).
|
|
8
|
+
*
|
|
9
|
+
* The runtime sends each beside the tool's result in
|
|
10
|
+
* `_meta["reactor.ui/display"]`, as every protocol display travels
|
|
11
|
+
* (REACTOR_UI RU-1): `application/vnd.datalayer.shield.diff+json` and
|
|
12
|
+
* `application/vnd.datalayer.shield.terminal+json`, typed in Python as
|
|
13
|
+
* `agent_runtimes.adapters.harness_displays`. This plugin contributes one
|
|
14
|
+
* renderer per type; it binds no tool, since a harness names its tools as it
|
|
15
|
+
* likes (*Edit hello.py*, *Run pytest*) and the display is what says what
|
|
16
|
+
* it is. The web chat and the embed mount it through the preset; the phone
|
|
17
|
+
* reads the same contract from `portable/displays` (MOBILE M-037).
|
|
18
|
+
*
|
|
19
|
+
* @module apps/plugins/shield-displays
|
|
20
|
+
*/
|
|
21
|
+
import { createElement } from 'react';
|
|
22
|
+
import { contribution, definePlugin } from '@datalayer/reactor';
|
|
23
|
+
import { Renderers } from '@datalayer/reactor/ui';
|
|
24
|
+
import { defineRenderer, } from '@datalayer/reactor/ui/react';
|
|
25
|
+
import { protocolDisplayOf, SHIELD_DIFF_TYPE, SHIELD_TERMINAL_TYPE, } from '../../../portable';
|
|
26
|
+
import { DiffCard } from './DiffCard';
|
|
27
|
+
import { TerminalCard } from './TerminalCard';
|
|
28
|
+
export const SHIELD_DISPLAYS_PLUGIN_NAME = '@datalayer/loop-plugin-shield-displays';
|
|
29
|
+
function Diff({ display, context, }) {
|
|
30
|
+
const read = protocolDisplayOf(display);
|
|
31
|
+
if (!read || read.kind !== 'diff') {
|
|
32
|
+
// Does not read as a diff: the chat's own row says what it can.
|
|
33
|
+
return createElement('span', null, context?.defaultUI ?? display.text);
|
|
34
|
+
}
|
|
35
|
+
return createElement(DiffCard, { key: display.id, display: read });
|
|
36
|
+
}
|
|
37
|
+
function Terminal({ display, context, }) {
|
|
38
|
+
const read = protocolDisplayOf(display);
|
|
39
|
+
if (!read || read.kind !== 'terminal') {
|
|
40
|
+
return createElement('span', null, context?.defaultUI ?? display.text);
|
|
41
|
+
}
|
|
42
|
+
return createElement(TerminalCard, { key: display.id, display: read });
|
|
43
|
+
}
|
|
44
|
+
/** The plugin: two renderers, no tool binding. */
|
|
45
|
+
export function createShieldDisplaysPlugin() {
|
|
46
|
+
const plugin = definePlugin({
|
|
47
|
+
name: SHIELD_DISPLAYS_PLUGIN_NAME,
|
|
48
|
+
displayName: 'Diffs and terminals',
|
|
49
|
+
description: 'What a coding agent changed, as a unified diff, and what it ran, with its output.',
|
|
50
|
+
octicon: 'diff',
|
|
51
|
+
emoji: '\u{1F6E1}',
|
|
52
|
+
contributes: [
|
|
53
|
+
contribution(Renderers, defineRenderer({ type: SHIELD_DIFF_TYPE, component: Diff }), { id: 'shield-diff' }),
|
|
54
|
+
contribution(Renderers, defineRenderer({ type: SHIELD_TERMINAL_TYPE, component: Terminal }), { id: 'shield-terminal' }),
|
|
55
|
+
],
|
|
56
|
+
});
|
|
57
|
+
return plugin;
|
|
58
|
+
}
|
|
59
|
+
/** The instance the preset mounts. */
|
|
60
|
+
export const ShieldDisplaysPlugin = createShieldDisplaysPlugin();
|
|
61
|
+
export { DiffCard } from './DiffCard';
|
|
62
|
+
export { TerminalCard } from './TerminalCard';
|
|
63
|
+
export default ShieldDisplaysPlugin;
|
package/lib/apps/presets.js
CHANGED
|
@@ -40,6 +40,7 @@ import { ChatPlugin, } from './plugins/chat';
|
|
|
40
40
|
import { ChatHeaderPlugin } from './plugins/chat-header';
|
|
41
41
|
import { A2uiSurfacePlugin } from './plugins/a2ui-surface';
|
|
42
42
|
import { PapSignInPlugin } from './plugins/pap-sign-in';
|
|
43
|
+
import { ShieldDisplaysPlugin } from './plugins/shield-displays';
|
|
43
44
|
import { ChatViewPlugin } from './plugins/chat-view';
|
|
44
45
|
import { DocumentViewPlugin } from './plugins/document-view';
|
|
45
46
|
import { InputPromptPlugin } from './plugins/input-prompt';
|
|
@@ -109,6 +110,9 @@ export function loopPlugins(options = {}) {
|
|
|
109
110
|
// *Sign in at a company*, where the gateway's `pap_sign_in` answers: its
|
|
110
111
|
// own renderer and binding, beside the surfaces rather than inside them.
|
|
111
112
|
PapSignInPlugin,
|
|
113
|
+
// A coding agent's diff and terminal, where its tool results carry them
|
|
114
|
+
// (SHIELD §4.3): two renderers, no binding — the display says what it is.
|
|
115
|
+
ShieldDisplaysPlugin,
|
|
112
116
|
// One footer icon per view, in the composer where the writing hand
|
|
113
117
|
// already is. Each editor's icon withdraws while its editor is not
|
|
114
118
|
// contributed, so mounting these unconditionally costs an absent editor
|
|
@@ -18,6 +18,11 @@
|
|
|
18
18
|
* where the conversation stands — participation.
|
|
19
19
|
* - **A handoff** (`pap_handoff`'s answer): handed to the company's people,
|
|
20
20
|
* or why not.
|
|
21
|
+
* - **A diff** (`…shield.diff+json`, SHIELD §4.3): what a coding agent
|
|
22
|
+
* changed in one file — its path, what it held, what it holds now, the
|
|
23
|
+
* file's language — drawn as a unified diff (`unifiedDiffLines`).
|
|
24
|
+
* - **A terminal** (`…shield.terminal+json`): what it ran — the command, the
|
|
25
|
+
* output kept after release, the exit code.
|
|
21
26
|
*
|
|
22
27
|
* Anything else — an unknown type, a known one whose data does not read — is
|
|
23
28
|
* `unknown`: its type, version and model-safe text, nothing drawn from its
|
|
@@ -30,6 +35,8 @@ export declare const PAP_SIGN_IN_TYPE = "application/vnd.datalayer.pap.sign-in+j
|
|
|
30
35
|
export declare const PAP_CONFIRM_TYPE = "application/vnd.datalayer.pap.confirm+json";
|
|
31
36
|
export declare const PAP_OUTCOME_TYPE = "application/vnd.datalayer.pap.outcome+json";
|
|
32
37
|
export declare const PAP_REPLIES_TYPE = "application/vnd.datalayer.pap.replies+json";
|
|
38
|
+
export declare const SHIELD_DIFF_TYPE = "application/vnd.datalayer.shield.diff+json";
|
|
39
|
+
export declare const SHIELD_TERMINAL_TYPE = "application/vnd.datalayer.shield.terminal+json";
|
|
33
40
|
/** Where a tool result carries its display for the page. */
|
|
34
41
|
export declare const DISPLAY_META_KEY = "reactor.ui/display";
|
|
35
42
|
/** What confirming an operation does, as an application's Rules class it. */
|
|
@@ -86,6 +93,27 @@ export type ProtocolDisplay = ({
|
|
|
86
93
|
handed: boolean;
|
|
87
94
|
responder: 'agent' | 'human' | null;
|
|
88
95
|
note: string;
|
|
96
|
+
} | {
|
|
97
|
+
kind: 'diff';
|
|
98
|
+
id: string;
|
|
99
|
+
/** The file, as the agent named it, relative to its workspace. */
|
|
100
|
+
path: string;
|
|
101
|
+
/** What it held; `null` for a file that did not exist. */
|
|
102
|
+
oldText: string | null;
|
|
103
|
+
newText: string;
|
|
104
|
+
/** A highlighter's language name, from the file's suffix (`text` when none). */
|
|
105
|
+
language: string;
|
|
106
|
+
} | {
|
|
107
|
+
kind: 'terminal';
|
|
108
|
+
id: string;
|
|
109
|
+
terminalId: string;
|
|
110
|
+
/** What it ran, when known. */
|
|
111
|
+
command: string;
|
|
112
|
+
/** The output, kept after the terminal was released. */
|
|
113
|
+
output: string;
|
|
114
|
+
/** `null` while it runs. */
|
|
115
|
+
exitCode: number | null;
|
|
116
|
+
released: boolean;
|
|
89
117
|
} | {
|
|
90
118
|
kind: 'unknown';
|
|
91
119
|
id: string;
|
|
@@ -107,3 +135,22 @@ export declare function displayOfToolResult(toolName: string, result: unknown, i
|
|
|
107
135
|
export declare function displayLine(display: ProtocolDisplay): string;
|
|
108
136
|
/** Who wrote a reply, said apart from the person's words and the agent's summaries (PAP-7.8-01). */
|
|
109
137
|
export declare const replyAuthor: (company: string, sender: "agent" | "human") => string;
|
|
138
|
+
/** The language a file's suffix says, for highlighting; `text` when none. */
|
|
139
|
+
export declare function languageOf(path: string): string;
|
|
140
|
+
/** One line of a unified diff: kept, added or removed. */
|
|
141
|
+
export type DiffLine = {
|
|
142
|
+
kind: 'context' | 'add' | 'remove';
|
|
143
|
+
text: string;
|
|
144
|
+
};
|
|
145
|
+
/** Over this many lines on a side, the diff is shown whole (all removed, all added), not computed. */
|
|
146
|
+
export declare const DIFF_LINES_LIMIT = 2000;
|
|
147
|
+
/**
|
|
148
|
+
* The lines of a unified diff between what a file held and what it holds,
|
|
149
|
+
* by a longest common subsequence of lines; a file over `DIFF_LINES_LIMIT`
|
|
150
|
+
* lines on a side is shown removed and added whole.
|
|
151
|
+
*/
|
|
152
|
+
export declare function unifiedDiffLines(oldText: string | null, newText: string): DiffLine[];
|
|
153
|
+
/** The unified diff as text (`--- a/path`, `+++ b/path`, one sign per line), as a terminal shows it. */
|
|
154
|
+
export declare function unifiedDiffText(display: Extract<ProtocolDisplay, {
|
|
155
|
+
kind: 'diff';
|
|
156
|
+
}>): string;
|
package/lib/portable/displays.js
CHANGED
|
@@ -22,6 +22,11 @@
|
|
|
22
22
|
* where the conversation stands — participation.
|
|
23
23
|
* - **A handoff** (`pap_handoff`'s answer): handed to the company's people,
|
|
24
24
|
* or why not.
|
|
25
|
+
* - **A diff** (`…shield.diff+json`, SHIELD §4.3): what a coding agent
|
|
26
|
+
* changed in one file — its path, what it held, what it holds now, the
|
|
27
|
+
* file's language — drawn as a unified diff (`unifiedDiffLines`).
|
|
28
|
+
* - **A terminal** (`…shield.terminal+json`): what it ran — the command, the
|
|
29
|
+
* output kept after release, the exit code.
|
|
25
30
|
*
|
|
26
31
|
* Anything else — an unknown type, a known one whose data does not read — is
|
|
27
32
|
* `unknown`: its type, version and model-safe text, nothing drawn from its
|
|
@@ -34,6 +39,8 @@ export const PAP_SIGN_IN_TYPE = 'application/vnd.datalayer.pap.sign-in+json';
|
|
|
34
39
|
export const PAP_CONFIRM_TYPE = 'application/vnd.datalayer.pap.confirm+json';
|
|
35
40
|
export const PAP_OUTCOME_TYPE = 'application/vnd.datalayer.pap.outcome+json';
|
|
36
41
|
export const PAP_REPLIES_TYPE = 'application/vnd.datalayer.pap.replies+json';
|
|
42
|
+
export const SHIELD_DIFF_TYPE = 'application/vnd.datalayer.shield.diff+json';
|
|
43
|
+
export const SHIELD_TERMINAL_TYPE = 'application/vnd.datalayer.shield.terminal+json';
|
|
37
44
|
/** Where a tool result carries its display for the page. */
|
|
38
45
|
export const DISPLAY_META_KEY = 'reactor.ui/display';
|
|
39
46
|
const record = (value) => value && typeof value === 'object' && !Array.isArray(value)
|
|
@@ -185,6 +192,46 @@ export function protocolDisplayOf(wire) {
|
|
|
185
192
|
responder: who(data.responder),
|
|
186
193
|
};
|
|
187
194
|
}
|
|
195
|
+
case SHIELD_DIFF_TYPE: {
|
|
196
|
+
const path = words(data.path);
|
|
197
|
+
if (!path ||
|
|
198
|
+
typeof data.new_text !== 'string' ||
|
|
199
|
+
!(data.old_text === null ||
|
|
200
|
+
data.old_text === undefined ||
|
|
201
|
+
typeof data.old_text === 'string')) {
|
|
202
|
+
return unknown;
|
|
203
|
+
}
|
|
204
|
+
return {
|
|
205
|
+
kind: 'diff',
|
|
206
|
+
id,
|
|
207
|
+
path,
|
|
208
|
+
oldText: typeof data.old_text === 'string' ? data.old_text : null,
|
|
209
|
+
newText: data.new_text,
|
|
210
|
+
language: words(data.language) || languageOf(path),
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
case SHIELD_TERMINAL_TYPE: {
|
|
214
|
+
const terminalId = words(data.terminal_id);
|
|
215
|
+
if (!terminalId)
|
|
216
|
+
return unknown;
|
|
217
|
+
const exitCode = typeof data.exit_code === 'number' && Number.isInteger(data.exit_code)
|
|
218
|
+
? data.exit_code
|
|
219
|
+
: null;
|
|
220
|
+
if (data.exit_code !== null &&
|
|
221
|
+
data.exit_code !== undefined &&
|
|
222
|
+
exitCode === null) {
|
|
223
|
+
return unknown;
|
|
224
|
+
}
|
|
225
|
+
return {
|
|
226
|
+
kind: 'terminal',
|
|
227
|
+
id,
|
|
228
|
+
terminalId,
|
|
229
|
+
command: text(data.command),
|
|
230
|
+
output: text(data.output),
|
|
231
|
+
exitCode,
|
|
232
|
+
released: data.released === true,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
188
235
|
default:
|
|
189
236
|
return unknown;
|
|
190
237
|
}
|
|
@@ -245,6 +292,21 @@ export function displayLine(display) {
|
|
|
245
292
|
return display.handed
|
|
246
293
|
? `Handed to ${display.responder === 'human' ? 'one of the company’s people' : 'the company'}`
|
|
247
294
|
: display.note || 'Nothing was handed over.';
|
|
295
|
+
case 'diff': {
|
|
296
|
+
const added = countLines(display.newText);
|
|
297
|
+
if (display.oldText === null) {
|
|
298
|
+
return `Wrote ${display.path} (${added} lines)`;
|
|
299
|
+
}
|
|
300
|
+
return `Changed ${display.path} (+${added} −${countLines(display.oldText)} lines)`;
|
|
301
|
+
}
|
|
302
|
+
case 'terminal': {
|
|
303
|
+
const what = display.command
|
|
304
|
+
? `Ran \`${display.command}\``
|
|
305
|
+
: `Terminal ${display.terminalId}`;
|
|
306
|
+
return display.exitCode === null
|
|
307
|
+
? `${what}: running`
|
|
308
|
+
: `${what}: exit ${display.exitCode}`;
|
|
309
|
+
}
|
|
248
310
|
case 'unknown':
|
|
249
311
|
return display.text || `A display this app cannot draw (${display.type})`;
|
|
250
312
|
}
|
|
@@ -259,3 +321,111 @@ const OUTCOME_LEADS = {
|
|
|
259
321
|
export const replyAuthor = (company, sender) => sender === 'human'
|
|
260
322
|
? `${company}, one of its people`
|
|
261
323
|
: `${company}, its agent`;
|
|
324
|
+
// -- a diff and a terminal (SHIELD §4.3, SH-16) ------------------------------------
|
|
325
|
+
const countLines = (value) => value === '' ? 0 : value.split('\n').length - (value.endsWith('\n') ? 1 : 0);
|
|
326
|
+
/** A file's language from its suffix, the same table the runtime uses. */
|
|
327
|
+
const LANGUAGES = {
|
|
328
|
+
py: 'python',
|
|
329
|
+
pyi: 'python',
|
|
330
|
+
ts: 'typescript',
|
|
331
|
+
tsx: 'tsx',
|
|
332
|
+
js: 'javascript',
|
|
333
|
+
jsx: 'jsx',
|
|
334
|
+
mjs: 'javascript',
|
|
335
|
+
json: 'json',
|
|
336
|
+
md: 'markdown',
|
|
337
|
+
mdx: 'markdown',
|
|
338
|
+
yaml: 'yaml',
|
|
339
|
+
yml: 'yaml',
|
|
340
|
+
toml: 'toml',
|
|
341
|
+
sh: 'bash',
|
|
342
|
+
bash: 'bash',
|
|
343
|
+
css: 'css',
|
|
344
|
+
scss: 'scss',
|
|
345
|
+
html: 'html',
|
|
346
|
+
xml: 'xml',
|
|
347
|
+
sql: 'sql',
|
|
348
|
+
rs: 'rust',
|
|
349
|
+
go: 'go',
|
|
350
|
+
java: 'java',
|
|
351
|
+
c: 'c',
|
|
352
|
+
h: 'c',
|
|
353
|
+
cpp: 'cpp',
|
|
354
|
+
rb: 'ruby',
|
|
355
|
+
ipynb: 'json',
|
|
356
|
+
txt: 'text',
|
|
357
|
+
};
|
|
358
|
+
/** The language a file's suffix says, for highlighting; `text` when none. */
|
|
359
|
+
export function languageOf(path) {
|
|
360
|
+
const name = path.split('/').pop() ?? '';
|
|
361
|
+
if (name === 'Dockerfile' || name === 'Makefile')
|
|
362
|
+
return name.toLowerCase();
|
|
363
|
+
const dot = name.lastIndexOf('.');
|
|
364
|
+
return dot > 0
|
|
365
|
+
? (LANGUAGES[name.slice(dot + 1).toLowerCase()] ?? 'text')
|
|
366
|
+
: 'text';
|
|
367
|
+
}
|
|
368
|
+
/** Over this many lines on a side, the diff is shown whole (all removed, all added), not computed. */
|
|
369
|
+
export const DIFF_LINES_LIMIT = 2000;
|
|
370
|
+
const lines = (value) => value === '' ? [] : value.replace(/\n$/, '').split('\n');
|
|
371
|
+
/**
|
|
372
|
+
* The lines of a unified diff between what a file held and what it holds,
|
|
373
|
+
* by a longest common subsequence of lines; a file over `DIFF_LINES_LIMIT`
|
|
374
|
+
* lines on a side is shown removed and added whole.
|
|
375
|
+
*/
|
|
376
|
+
export function unifiedDiffLines(oldText, newText) {
|
|
377
|
+
const before = oldText === null ? [] : lines(oldText);
|
|
378
|
+
const after = lines(newText);
|
|
379
|
+
if (before.length > DIFF_LINES_LIMIT || after.length > DIFF_LINES_LIMIT) {
|
|
380
|
+
return [
|
|
381
|
+
...before.map(text => ({ kind: 'remove', text })),
|
|
382
|
+
...after.map(text => ({ kind: 'add', text })),
|
|
383
|
+
];
|
|
384
|
+
}
|
|
385
|
+
// The LCS table, one row per line of `before`.
|
|
386
|
+
const table = [];
|
|
387
|
+
for (let i = 0; i <= before.length; i++) {
|
|
388
|
+
table.push(new Uint32Array(after.length + 1));
|
|
389
|
+
}
|
|
390
|
+
for (let i = before.length - 1; i >= 0; i--) {
|
|
391
|
+
for (let j = after.length - 1; j >= 0; j--) {
|
|
392
|
+
table[i][j] =
|
|
393
|
+
before[i] === after[j]
|
|
394
|
+
? table[i + 1][j + 1] + 1
|
|
395
|
+
: Math.max(table[i + 1][j], table[i][j + 1]);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const out = [];
|
|
399
|
+
let i = 0;
|
|
400
|
+
let j = 0;
|
|
401
|
+
while (i < before.length && j < after.length) {
|
|
402
|
+
if (before[i] === after[j]) {
|
|
403
|
+
out.push({ kind: 'context', text: before[i] });
|
|
404
|
+
i++;
|
|
405
|
+
j++;
|
|
406
|
+
}
|
|
407
|
+
else if (table[i + 1][j] >= table[i][j + 1]) {
|
|
408
|
+
out.push({ kind: 'remove', text: before[i] });
|
|
409
|
+
i++;
|
|
410
|
+
}
|
|
411
|
+
else {
|
|
412
|
+
out.push({ kind: 'add', text: after[j] });
|
|
413
|
+
j++;
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
while (i < before.length)
|
|
417
|
+
out.push({ kind: 'remove', text: before[i++] });
|
|
418
|
+
while (j < after.length)
|
|
419
|
+
out.push({ kind: 'add', text: after[j++] });
|
|
420
|
+
return out;
|
|
421
|
+
}
|
|
422
|
+
/** The unified diff as text (`--- a/path`, `+++ b/path`, one sign per line), as a terminal shows it. */
|
|
423
|
+
export function unifiedDiffText(display) {
|
|
424
|
+
const from = display.oldText === null ? '/dev/null' : `a/${display.path}`;
|
|
425
|
+
const signs = { context: ' ', add: '+', remove: '-' };
|
|
426
|
+
return [
|
|
427
|
+
`--- ${from}`,
|
|
428
|
+
`+++ b/${display.path}`,
|
|
429
|
+
...unifiedDiffLines(display.oldText, display.newText).map(line => `${signs[line.kind]}${line.text}`),
|
|
430
|
+
].join('\n');
|
|
431
|
+
}
|
|
@@ -8,4 +8,13 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { type ConversationState } from './conversation';
|
|
10
10
|
/** A fresh copy of the fixture conversation. */
|
|
11
|
+
/**
|
|
12
|
+
* A coding agent's diff and terminal, as the runtime sends them beside a tool
|
|
13
|
+
* result (`_meta["reactor.ui/display"]`, SHIELD §4.3, SH-16): what the phone
|
|
14
|
+
* draws for MOBILE M-037, read by `protocolDisplayOf`.
|
|
15
|
+
*/
|
|
16
|
+
export declare function shieldDisplayFixtures(): {
|
|
17
|
+
diff: Record<string, unknown>;
|
|
18
|
+
terminal: Record<string, unknown>;
|
|
19
|
+
};
|
|
11
20
|
export declare function conversationFixture(): ConversationState;
|
package/lib/portable/fixtures.js
CHANGED
|
@@ -13,6 +13,42 @@
|
|
|
13
13
|
import { emptyConversation } from './conversation';
|
|
14
14
|
import { RUN_END_WORDS } from './records';
|
|
15
15
|
/** A fresh copy of the fixture conversation. */
|
|
16
|
+
/**
|
|
17
|
+
* A coding agent's diff and terminal, as the runtime sends them beside a tool
|
|
18
|
+
* result (`_meta["reactor.ui/display"]`, SHIELD §4.3, SH-16): what the phone
|
|
19
|
+
* draws for MOBILE M-037, read by `protocolDisplayOf`.
|
|
20
|
+
*/
|
|
21
|
+
export function shieldDisplayFixtures() {
|
|
22
|
+
return {
|
|
23
|
+
diff: {
|
|
24
|
+
type: 'application/vnd.datalayer.shield.diff+json',
|
|
25
|
+
version: 1,
|
|
26
|
+
id: 'c1-diff',
|
|
27
|
+
text: 'Changed hello.py (+2 −1 lines)',
|
|
28
|
+
data: {
|
|
29
|
+
path: 'hello.py',
|
|
30
|
+
old_text: 'print("hi")\n',
|
|
31
|
+
new_text: 'def hello():\n print("hi, shield")\n',
|
|
32
|
+
language: 'python',
|
|
33
|
+
},
|
|
34
|
+
actions: [],
|
|
35
|
+
},
|
|
36
|
+
terminal: {
|
|
37
|
+
type: 'application/vnd.datalayer.shield.terminal+json',
|
|
38
|
+
version: 1,
|
|
39
|
+
id: 'c3-terminal',
|
|
40
|
+
text: 'Ran `pytest -q`: exit 0',
|
|
41
|
+
data: {
|
|
42
|
+
terminal_id: 'term_1',
|
|
43
|
+
command: 'pytest -q',
|
|
44
|
+
output: '3 passed in 0.12s\n',
|
|
45
|
+
exit_code: 0,
|
|
46
|
+
released: true,
|
|
47
|
+
},
|
|
48
|
+
actions: [],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
16
52
|
export function conversationFixture() {
|
|
17
53
|
return {
|
|
18
54
|
...emptyConversation(),
|
package/lib/portable/index.d.ts
CHANGED
|
@@ -17,7 +17,8 @@ export { conversationEventsOf, sanitizeAssistantContent, snapshotOfMessages, typ
|
|
|
17
17
|
export { PRESENCE_LINES, presenceOfConversation, presenceState, presenceToolOf, type PresenceState, type PresenceTool, } from './presence';
|
|
18
18
|
export { ANSWER_BELOW, ANSWER_COMPLETE, composerOf, droppedWords, ENDED_WORDS, messageLabel, OPENING_WORDS, presenceLine, showsQuestion, stepLine, TOOL_STATUS_WORDS, toolLabel, type ComposerProps, type ComposerState, type ConversationViewProps, type ErrorItem, type ErrorRowProps, type MessageItem, type MessageRowProps, type PresenceLineProps, type ProtocolDisplayProps, type QuestionCardProps, type StepItem, type StepRowProps, type ToolItem, type ToolRowProps, type TurnEndItem, type TurnEndRowProps, } from './views';
|
|
19
19
|
export { conversationFixture } from './fixtures';
|
|
20
|
-
export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor, type ActionClass, type ProtocolDisplay, } from './displays';
|
|
20
|
+
export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor, type ActionClass, type ProtocolDisplay, DIFF_LINES_LIMIT, SHIELD_DIFF_TYPE, SHIELD_TERMINAL_TYPE, languageOf, unifiedDiffLines, unifiedDiffText, type DiffLine, } from './displays';
|
|
21
|
+
export { shieldDisplayFixtures } from './fixtures';
|
|
21
22
|
export { isPapSignInTool, PAP_SIGN_IN_WORDS, readPapSignIn, safeSignInUrl, type PapSignInWaiting, } from './signIn';
|
|
22
23
|
export { DIRECT_MAX, DIRECT_WORDS, directAnswerOf, directAuthor, directClient, openDirect, withDirectAnswer, type DirectAnswer, type DirectClient, type DirectClientOptions, type DirectLine, type DirectOpen, type DirectReply, type DirectTarget, } from './direct';
|
|
23
24
|
export { SIGN_IN_POLL_MS, SIGN_IN_STATUS_PATH, signInStandsOf, signInStatusClient, type SignInStands, } from './signInStatus';
|
package/lib/portable/index.js
CHANGED
|
@@ -29,7 +29,10 @@ export { PRESENCE_LINES, presenceOfConversation, presenceState, presenceToolOf,
|
|
|
29
29
|
export { ANSWER_BELOW, ANSWER_COMPLETE, composerOf, droppedWords, ENDED_WORDS, messageLabel, OPENING_WORDS, presenceLine, showsQuestion, stepLine, TOOL_STATUS_WORDS, toolLabel, } from './views';
|
|
30
30
|
export { conversationFixture } from './fixtures';
|
|
31
31
|
// The protocol displays a run's tool results carry (MOBILE M-037).
|
|
32
|
-
export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor,
|
|
32
|
+
export { DISPLAY_META_KEY, displayLine, displayOfToolResult, PAP_CONFIRM_TYPE, PAP_HANDOFF_TOOL, PAP_OUTCOME_TYPE, PAP_REPLIES_TYPE, PAP_SIGN_IN_TYPE, protocolDisplayOf, replyAuthor,
|
|
33
|
+
// A coding agent's diff and terminal (SHIELD §4.3, SH-16).
|
|
34
|
+
DIFF_LINES_LIMIT, SHIELD_DIFF_TYPE, SHIELD_TERMINAL_TYPE, languageOf, unifiedDiffLines, unifiedDiffText, } from './displays';
|
|
35
|
+
export { shieldDisplayFixtures } from './fixtures';
|
|
33
36
|
export { isPapSignInTool, PAP_SIGN_IN_WORDS, readPapSignIn, safeSignInUrl, } from './signIn';
|
|
34
37
|
// A Direct Conversation the person types in themselves (PAP_DATALAYER P-08, MOBILE M-038).
|
|
35
38
|
export { DIRECT_MAX, DIRECT_WORDS, directAnswerOf, directAuthor, directClient, openDirect, withDirectAnswer, } from './direct';
|
package/lib/portable/views.d.ts
CHANGED
|
@@ -61,7 +61,9 @@ export type QuestionCardProps = {
|
|
|
61
61
|
};
|
|
62
62
|
/**
|
|
63
63
|
* A protocol display under its tool row (MOBILE M-037): what it shows is
|
|
64
|
-
* `displayLine`'s and the display's own
|
|
64
|
+
* `displayLine`'s and the display's own — a coding agent's `diff` as a
|
|
65
|
+
* unified diff in the file's language (`unifiedDiffLines`), its `terminal`
|
|
66
|
+
* as the command and the output kept (SHIELD §4.3); what the person does goes out
|
|
65
67
|
* through the host — a sign-in page opened outside the conversation, an
|
|
66
68
|
* operation answered where approvals are, *I have signed in* said as their
|
|
67
69
|
* next message. Nothing is answered from the card itself.
|
|
@@ -41,6 +41,7 @@ export declare const EXAMPLE_FULL_AGENTSPEC_0_0_1: Agentspec;
|
|
|
41
41
|
export declare const EXAMPLE_GPT_AND_CLAUDE_COLLABORATE_AGENTSPEC_0_0_1: Agentspec;
|
|
42
42
|
export declare const EXAMPLE_GUARDRAILS_AGENTSPEC_0_0_1: Agentspec;
|
|
43
43
|
export declare const EXAMPLE_HAIKU_GENERATIVE_UI_AGENTSPEC_0_0_1: Agentspec;
|
|
44
|
+
export declare const EXAMPLE_HARNESS_CLAUDE_CODE_AGENTSPEC_0_0_1: Agentspec;
|
|
44
45
|
export declare const EXAMPLE_HOOKS_AGENTSPEC_0_0_1: Agentspec;
|
|
45
46
|
export declare const EXAMPLE_HUMAN_APPROVED_AUTOMATION_AGENTSPEC_0_0_1: Agentspec;
|
|
46
47
|
export declare const EXAMPLE_HUMAN_IN_THE_LOOP_AGENTSPEC_0_0_1: Agentspec;
|
|
@@ -2582,6 +2582,54 @@ beautifully. Just acknowledge that you've created the haiku.
|
|
|
2582
2582
|
parameters: undefined,
|
|
2583
2583
|
subagents: undefined,
|
|
2584
2584
|
};
|
|
2585
|
+
export const EXAMPLE_HARNESS_CLAUDE_CODE_AGENTSPEC_0_0_1 = {
|
|
2586
|
+
id: 'example-harness-claude-code',
|
|
2587
|
+
version: '0.0.1',
|
|
2588
|
+
name: 'A Claude Code Agent',
|
|
2589
|
+
description: `Claude Code as a Datalayer agent: launched in the runtime over the Agent Client Protocol, its files, terminals and permissions decided by the Shield under the application's Rules, every action in the record. Needs the account's ANTHROPIC_API_KEY.`,
|
|
2590
|
+
tags: ['coding', 'harness', 'acp'],
|
|
2591
|
+
domain: undefined,
|
|
2592
|
+
enabled: false,
|
|
2593
|
+
model: undefined,
|
|
2594
|
+
mcpServers: [],
|
|
2595
|
+
skills: [].filter(Boolean),
|
|
2596
|
+
backendTools: [],
|
|
2597
|
+
frontendTools: [],
|
|
2598
|
+
environmentName: 'ai-agents-env',
|
|
2599
|
+
icon: undefined,
|
|
2600
|
+
emoji: undefined,
|
|
2601
|
+
color: undefined,
|
|
2602
|
+
suggestions: [],
|
|
2603
|
+
welcomeMessage: undefined,
|
|
2604
|
+
welcomeNotebook: undefined,
|
|
2605
|
+
welcomeDocument: undefined,
|
|
2606
|
+
sandboxVariant: 'jupyter-server',
|
|
2607
|
+
harness: 'claude-code',
|
|
2608
|
+
systemPrompt: undefined,
|
|
2609
|
+
systemPromptCodemodeAddons: undefined,
|
|
2610
|
+
goal: undefined,
|
|
2611
|
+
delegable: [],
|
|
2612
|
+
protocol: undefined,
|
|
2613
|
+
ui: [],
|
|
2614
|
+
protocols: [],
|
|
2615
|
+
trigger: undefined,
|
|
2616
|
+
modelConfig: undefined,
|
|
2617
|
+
mcpServerTools: undefined,
|
|
2618
|
+
guardrails: undefined,
|
|
2619
|
+
evals: undefined,
|
|
2620
|
+
codemode: undefined,
|
|
2621
|
+
output: undefined,
|
|
2622
|
+
advanced: undefined,
|
|
2623
|
+
checkpoints: undefined,
|
|
2624
|
+
authorizationPolicy: undefined,
|
|
2625
|
+
notifications: undefined,
|
|
2626
|
+
memory: 'ephemeral',
|
|
2627
|
+
preHooks: undefined,
|
|
2628
|
+
postHooks: undefined,
|
|
2629
|
+
toolHooks: undefined,
|
|
2630
|
+
parameters: undefined,
|
|
2631
|
+
subagents: undefined,
|
|
2632
|
+
};
|
|
2585
2633
|
export const EXAMPLE_HOOKS_AGENTSPEC_0_0_1 = {
|
|
2586
2634
|
id: 'example-hooks',
|
|
2587
2635
|
version: '0.0.1',
|
|
@@ -13514,6 +13562,7 @@ export const AGENTSPECS = {
|
|
|
13514
13562
|
'example-gpt-and-claude-collaborate': EXAMPLE_GPT_AND_CLAUDE_COLLABORATE_AGENTSPEC_0_0_1,
|
|
13515
13563
|
'example-guardrails': EXAMPLE_GUARDRAILS_AGENTSPEC_0_0_1,
|
|
13516
13564
|
'example-haiku-generative-ui': EXAMPLE_HAIKU_GENERATIVE_UI_AGENTSPEC_0_0_1,
|
|
13565
|
+
'example-harness-claude-code': EXAMPLE_HARNESS_CLAUDE_CODE_AGENTSPEC_0_0_1,
|
|
13517
13566
|
'example-hooks': EXAMPLE_HOOKS_AGENTSPEC_0_0_1,
|
|
13518
13567
|
'example-human-approved-automation': EXAMPLE_HUMAN_APPROVED_AUTOMATION_AGENTSPEC_0_0_1,
|
|
13519
13568
|
'example-human-in-the-loop': EXAMPLE_HUMAN_IN_THE_LOOP_AGENTSPEC_0_0_1,
|