@jigging/agent-acp 0.0.0 → 0.1.0-alpha.3

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.
@@ -0,0 +1,73 @@
1
+ {
2
+ "$schema": "https://flow.jig.md/schemas/channel-contract-0.schema.json",
3
+ "id": "https://jig.md/contracts/agent-commands",
4
+ "version": "0.1.0",
5
+ "semantics": "Direct controls for an explicitly conversational Agent Run. Initial invocation input starts turn 0. Prompt names the next sequential turn; interrupt names the running turn; close names the last settled turn. Controls are accepted or rejected separately from turn settlement and final invocation cleanup. Delivery is not dispatch or completion. No automatic queue or replay.",
6
+ "item": {
7
+ "oneOf": [
8
+ {
9
+ "type": "object",
10
+ "properties": {
11
+ "type": {
12
+ "const": "prompt"
13
+ },
14
+ "turn": {
15
+ "type": "integer",
16
+ "minimum": 0,
17
+ "maximum": 7
18
+ },
19
+ "input": {
20
+ "type": "object",
21
+ "properties": {
22
+ "instructions": {
23
+ "type": "string",
24
+ "minLength": 1,
25
+ "maxLength": 1048576
26
+ },
27
+ "responseSchema": {
28
+ "type": "object"
29
+ },
30
+ "guidance": {
31
+ "type": "array",
32
+ "maxItems": 64,
33
+ "items": {
34
+ "type": "object",
35
+ "properties": {
36
+ "label": {
37
+ "type": "string",
38
+ "minLength": 1
39
+ },
40
+ "text": {
41
+ "type": "string"
42
+ }
43
+ },
44
+ "required": ["label", "text"],
45
+ "additionalProperties": false
46
+ }
47
+ }
48
+ },
49
+ "required": ["instructions"],
50
+ "additionalProperties": false
51
+ }
52
+ },
53
+ "required": ["type", "turn", "input"],
54
+ "additionalProperties": false
55
+ },
56
+ {
57
+ "type": "object",
58
+ "properties": {
59
+ "type": {
60
+ "enum": ["interrupt", "close"]
61
+ },
62
+ "turn": {
63
+ "type": "integer",
64
+ "minimum": 0,
65
+ "maximum": 7
66
+ }
67
+ },
68
+ "required": ["type", "turn"],
69
+ "additionalProperties": false
70
+ }
71
+ ]
72
+ }
73
+ }
@@ -0,0 +1,156 @@
1
+ {
2
+ "$schema": "https://flow.jig.md/schemas/channel-contract-0.schema.json",
3
+ "id": "https://jig.md/contracts/agent-replies",
4
+ "version": "0.1.0",
5
+ "semantics": "Essential direct replies for conversational Agent Run. accepted acknowledges a control, not completed work; result is one settled Agent answer; cancelled settles a cancelled turn without an answer; error reports a settled but invalid answer. rejected performs no requested action. All items are at most 64 KiB; lost, oversized or persistently blocked replies fail the conversation rather than truncate results. End of stream is not native cleanup: await the enclosing invocation.",
6
+ "item": {
7
+ "oneOf": [
8
+ {
9
+ "type": "object",
10
+ "properties": {
11
+ "type": {
12
+ "const": "accepted"
13
+ },
14
+ "command": {
15
+ "enum": ["prompt", "interrupt", "close"]
16
+ },
17
+ "turn": {
18
+ "type": "integer",
19
+ "minimum": 0,
20
+ "maximum": 7
21
+ }
22
+ },
23
+ "required": ["type", "command", "turn"],
24
+ "additionalProperties": false
25
+ },
26
+ {
27
+ "type": "object",
28
+ "properties": {
29
+ "type": {
30
+ "const": "rejected"
31
+ },
32
+ "command": {
33
+ "enum": ["prompt", "interrupt", "close"]
34
+ },
35
+ "turn": {
36
+ "type": "integer",
37
+ "minimum": 0
38
+ },
39
+ "code": {
40
+ "enum": ["BUSY", "STALE_TURN", "TURN_LIMIT", "INVALID_INPUT", "NOT_RUNNING"]
41
+ }
42
+ },
43
+ "required": ["type", "command", "turn", "code"],
44
+ "additionalProperties": false
45
+ },
46
+ {
47
+ "type": "object",
48
+ "properties": {
49
+ "type": {
50
+ "const": "result"
51
+ },
52
+ "turn": {
53
+ "type": "integer",
54
+ "minimum": 0,
55
+ "maximum": 7
56
+ },
57
+ "result": {
58
+ "oneOf": [
59
+ {
60
+ "type": "object",
61
+ "properties": {
62
+ "outcome": {
63
+ "const": "done"
64
+ },
65
+ "output": {
66
+ "$ref": "#/$defs/AgentOutput"
67
+ }
68
+ },
69
+ "required": ["outcome", "output"],
70
+ "additionalProperties": false
71
+ },
72
+ {
73
+ "type": "object",
74
+ "properties": {
75
+ "outcome": {
76
+ "const": "blocked"
77
+ },
78
+ "output": {
79
+ "$ref": "#/$defs/AgentOutput"
80
+ }
81
+ },
82
+ "required": ["outcome", "output"],
83
+ "additionalProperties": false
84
+ },
85
+ {
86
+ "type": "object",
87
+ "properties": {
88
+ "outcome": {
89
+ "const": "limit"
90
+ },
91
+ "output": {
92
+ "$ref": "#/$defs/AgentOutput"
93
+ }
94
+ },
95
+ "required": ["outcome", "output"],
96
+ "additionalProperties": false
97
+ }
98
+ ]
99
+ }
100
+ },
101
+ "required": ["type", "turn", "result"],
102
+ "additionalProperties": false
103
+ },
104
+ {
105
+ "type": "object",
106
+ "properties": {
107
+ "type": {
108
+ "const": "cancelled"
109
+ },
110
+ "turn": {
111
+ "type": "integer",
112
+ "minimum": 0,
113
+ "maximum": 7
114
+ }
115
+ },
116
+ "required": ["type", "turn"],
117
+ "additionalProperties": false
118
+ },
119
+ {
120
+ "type": "object",
121
+ "properties": {
122
+ "type": {
123
+ "const": "error"
124
+ },
125
+ "turn": {
126
+ "type": "integer",
127
+ "minimum": 0,
128
+ "maximum": 7
129
+ },
130
+ "code": {
131
+ "const": "INVALID_RESULT"
132
+ },
133
+ "message": {
134
+ "type": "string"
135
+ }
136
+ },
137
+ "required": ["type", "turn", "code", "message"],
138
+ "additionalProperties": false
139
+ }
140
+ ]
141
+ },
142
+ "$defs": {
143
+ "AgentOutput": {
144
+ "type": "object",
145
+ "properties": {
146
+ "text": {
147
+ "type": "string",
148
+ "maxLength": 8388608
149
+ },
150
+ "structured": true
151
+ },
152
+ "required": ["text"],
153
+ "additionalProperties": false
154
+ }
155
+ }
156
+ }
@@ -0,0 +1,104 @@
1
+ {
2
+ "$schema": "https://flow.jig.md/schemas/invocation-contract-0.schema.json",
3
+ "id": "https://jig.md/contracts/finite-acp",
4
+ "version": "0.1.0",
5
+ "input": {
6
+ "oneOf": [
7
+ { "type": "null" },
8
+ {
9
+ "type": "object",
10
+ "properties": { "session": { "$ref": "#/$defs/SessionRequest" } },
11
+ "required": ["session"],
12
+ "additionalProperties": false
13
+ }
14
+ ]
15
+ },
16
+ "result": {
17
+ "type": "object",
18
+ "properties": {
19
+ "outcome": {
20
+ "const": "done"
21
+ },
22
+ "output": {
23
+ "type": "object",
24
+ "properties": {
25
+ "exitCode": {
26
+ "type": ["integer", "null"]
27
+ },
28
+ "signal": {
29
+ "type": ["string", "null"]
30
+ },
31
+ "cleanup": {
32
+ "const": "complete"
33
+ },
34
+ "stopReason": {
35
+ "enum": ["exited", "closed"]
36
+ },
37
+ "session": {
38
+ "$ref": "#/$defs/SessionReceipt"
39
+ }
40
+ },
41
+ "required": ["exitCode", "signal", "cleanup", "stopReason"],
42
+ "additionalProperties": false
43
+ }
44
+ },
45
+ "required": ["outcome", "output"],
46
+ "additionalProperties": false
47
+ },
48
+ "$defs": {
49
+ "SessionRequest": {
50
+ "oneOf": [
51
+ {
52
+ "type": "object",
53
+ "properties": { "retain": { "const": true }, "lifetime": { "const": "run" } },
54
+ "required": ["retain"],
55
+ "additionalProperties": false
56
+ },
57
+ {
58
+ "type": "object",
59
+ "properties": {
60
+ "restore": { "type": "string", "minLength": 36, "maxLength": 36 }
61
+ },
62
+ "required": ["restore"],
63
+ "additionalProperties": false
64
+ }
65
+ ]
66
+ },
67
+ "SessionReceipt": {
68
+ "oneOf": [
69
+ {
70
+ "type": "object",
71
+ "properties": {
72
+ "status": { "const": "retained" },
73
+ "reference": { "type": "string", "minLength": 36, "maxLength": 36 }
74
+ },
75
+ "required": ["status", "reference"],
76
+ "additionalProperties": false
77
+ },
78
+ {
79
+ "type": "object",
80
+ "properties": {
81
+ "status": { "const": "unavailable" },
82
+ "reason": {
83
+ "enum": ["not-cleanly-closed", "missing-history", "unsupported-history", "capacity"]
84
+ }
85
+ },
86
+ "required": ["status", "reason"],
87
+ "additionalProperties": false
88
+ }
89
+ ]
90
+ }
91
+ },
92
+ "channels": {
93
+ "requests": {
94
+ "direction": "receive",
95
+ "required": true,
96
+ "contract": "./requests.json"
97
+ },
98
+ "responses": {
99
+ "direction": "send",
100
+ "required": true,
101
+ "contract": "./responses.json"
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,24 @@
1
+ {
2
+ "$schema": "https://flow.jig.md/schemas/channel-contract-0.schema.json",
3
+ "id": "https://jig.md/contracts/finite-acp-requests",
4
+ "version": "0.1.0",
5
+ "semantics": "Required direct requests for one host-authorized finite ACP conversation. Each data item carries 1 to 8192 UTF-8 bytes, cut only between Unicode scalars. Concatenate in order until end:true to obtain one JSON/0 ACP frame. There is no interleaving, replay, binary data, or independent chunk identity. The host validates the complete frame and reviewed authority before dispatch; accepting a fragment does not establish dispatch. EOF inside a frame fails the resource. Clean EOF requests process settlement only after the allowed conversation has settled. Bounds and the finite operation profile are specified by the finite-acp contract documentation.",
6
+ "item": {
7
+ "type": "object",
8
+ "properties": {
9
+ "kind": {
10
+ "const": "data"
11
+ },
12
+ "text": {
13
+ "type": "string",
14
+ "minLength": 1,
15
+ "maxLength": 8192
16
+ },
17
+ "end": {
18
+ "type": "boolean"
19
+ }
20
+ },
21
+ "required": ["kind", "text", "end"],
22
+ "additionalProperties": false
23
+ }
24
+ }
@@ -0,0 +1,102 @@
1
+ {
2
+ "$schema": "https://flow.jig.md/schemas/channel-contract-0.schema.json",
3
+ "id": "https://jig.md/contracts/finite-acp-responses",
4
+ "version": "0.1.0",
5
+ "semantics": "Required direct responses for one host-authorized finite ACP conversation. Exactly one ready item precedes data. Ready describes non-secret reviewed configuration and an armed transport, not completed authentication or an Agent result. Data items frame host-validated and authority-filtered ACP responses and public updates using the same bounded framing as finite-acp-requests. Thoughts, tools, permissions, credentials and raw diagnostic fields are not forwarded. This stream is essential: loss or incomplete framing fails the resource, not merely observation. EOF is not successful execution; await the separate resource result and interpret the Agent result separately.",
6
+ "item": {
7
+ "oneOf": [
8
+ {
9
+ "type": "object",
10
+ "properties": {
11
+ "kind": {
12
+ "const": "ready"
13
+ },
14
+ "protocolVersion": {
15
+ "const": 1
16
+ },
17
+ "cwd": {
18
+ "const": "/work"
19
+ },
20
+ "configuration": {
21
+ "type": "array",
22
+ "maxItems": 16,
23
+ "items": {
24
+ "oneOf": [
25
+ {
26
+ "type": "object",
27
+ "properties": {
28
+ "configId": {
29
+ "type": "string",
30
+ "minLength": 1,
31
+ "maxLength": 1024
32
+ },
33
+ "value": {
34
+ "type": "string",
35
+ "minLength": 1,
36
+ "maxLength": 1024
37
+ }
38
+ },
39
+ "required": ["configId", "value"],
40
+ "additionalProperties": false
41
+ },
42
+ {
43
+ "type": "object",
44
+ "properties": {
45
+ "configId": {
46
+ "type": "string",
47
+ "minLength": 1,
48
+ "maxLength": 1024
49
+ },
50
+ "type": {
51
+ "const": "boolean"
52
+ },
53
+ "value": {
54
+ "type": "boolean"
55
+ }
56
+ },
57
+ "required": ["configId", "type", "value"],
58
+ "additionalProperties": false
59
+ }
60
+ ]
61
+ }
62
+ },
63
+ "modeId": {
64
+ "type": "string",
65
+ "minLength": 1,
66
+ "maxLength": 1024
67
+ },
68
+ "restoreSessionId": {
69
+ "type": "string",
70
+ "minLength": 1,
71
+ "maxLength": 1024
72
+ },
73
+ "maxTurns": {
74
+ "type": "integer",
75
+ "minimum": 1,
76
+ "maximum": 8
77
+ }
78
+ },
79
+ "required": ["kind", "protocolVersion", "cwd", "configuration", "maxTurns"],
80
+ "additionalProperties": false
81
+ },
82
+ {
83
+ "type": "object",
84
+ "properties": {
85
+ "kind": {
86
+ "const": "data"
87
+ },
88
+ "text": {
89
+ "type": "string",
90
+ "minLength": 1,
91
+ "maxLength": 8192
92
+ },
93
+ "end": {
94
+ "type": "boolean"
95
+ }
96
+ },
97
+ "required": ["kind", "text", "end"],
98
+ "additionalProperties": false
99
+ }
100
+ ]
101
+ }
102
+ }
@@ -0,0 +1,8 @@
1
+ import { prepareAgent } from '@jigging/agent-method';
2
+ import { type ChannelReceiver, type ChannelSender, type RunResult } from '@jigging/flow';
3
+ export interface ConversationPeer {
4
+ prompt(input: ReturnType<typeof prepareAgent>, turn?: number): Promise<RunResult>;
5
+ interrupt(): Promise<boolean>;
6
+ }
7
+ /** Application dialogue only. Native dispatch and settlement are enforced by the resource. */
8
+ export declare function converse(peer: ConversationPeer, initial: ReturnType<typeof prepareAgent>, commands: ChannelReceiver, replies: ChannelSender, maxTurns: number, signal: AbortSignal): Promise<RunResult>;
@@ -0,0 +1,155 @@
1
+ import { AgentMethodError, prepareAgent } from '@jigging/agent-method';
2
+ import { OperationError, } from '@jigging/flow';
3
+ /** Application dialogue only. Native dispatch and settlement are enforced by the resource. */
4
+ export async function converse(peer, initial, commands, replies, maxTurns, signal) {
5
+ const reads = new AbortController();
6
+ const readSignal = AbortSignal.any([signal, reads.signal]);
7
+ const read = () => commands.next({ signal: readSignal }).then((item) => ({ kind: 'command', item }), (error) => ({ kind: 'read-error', error }));
8
+ const reply = async (value) => {
9
+ if (new TextEncoder().encode(JSON.stringify(value)).byteLength > 65_536)
10
+ throw new OperationError('RESOURCE_EXHAUSTED', 'A conversation reply exceeds 64 KiB; request a smaller answer');
11
+ const timeout = AbortSignal.timeout(5_000);
12
+ try {
13
+ await replies.send(value, { signal: AbortSignal.any([signal, timeout]) });
14
+ }
15
+ catch (error) {
16
+ signal.throwIfAborted();
17
+ if (timeout.aborted)
18
+ throw new OperationError('DEADLINE_EXCEEDED', 'Conversation reply delivery did not settle within five seconds');
19
+ throw error;
20
+ }
21
+ };
22
+ let turn = 0;
23
+ let settledTurns = 0;
24
+ let interrupted = false;
25
+ let receivedCommands = 0;
26
+ const start = (input) => peer.prompt(input, turn).then((result) => ({ kind: 'turn', result }), (error) => ({ kind: 'turn-error', error }));
27
+ let active = start(initial);
28
+ let command = read();
29
+ try {
30
+ for (;;) {
31
+ const next = await Promise.race(active ? [active, command] : [command]);
32
+ signal.throwIfAborted();
33
+ if (next.kind === 'read-error')
34
+ throw next.error;
35
+ if (next.kind === 'turn' || next.kind === 'turn-error') {
36
+ active = undefined;
37
+ settledTurns += 1;
38
+ if (next.kind === 'turn') {
39
+ await reply({ type: 'result', turn, result: next.result });
40
+ }
41
+ else if (next.error instanceof OperationError && next.error.code === 'CANCELLED') {
42
+ await reply({ type: 'cancelled', turn });
43
+ }
44
+ else if (next.error instanceof AgentMethodError && next.error.code === 'INVALID_RESULT') {
45
+ await reply({
46
+ type: 'error',
47
+ turn,
48
+ code: 'INVALID_RESULT',
49
+ message: 'The settled Agent answer does not match the requested result',
50
+ });
51
+ }
52
+ else
53
+ throw next.error;
54
+ continue;
55
+ }
56
+ if (next.item.done)
57
+ throw new OperationError('DISCONNECTED', 'Conversation commands ended without an accepted close');
58
+ if (++receivedCommands > 64)
59
+ throw new OperationError('RESOURCE_EXHAUSTED', 'Conversation control limit is 64 commands');
60
+ command = read();
61
+ const received = next.item.value;
62
+ if (received === null || typeof received !== 'object' || Array.isArray(received))
63
+ throw new OperationError('INVALID_INPUT', 'Expected a conversation command');
64
+ const value = received;
65
+ const type = value.type;
66
+ if (!['prompt', 'interrupt', 'close'].includes(type) ||
67
+ typeof value.turn !== 'number' ||
68
+ !Number.isSafeInteger(value.turn) ||
69
+ value.turn < 0 ||
70
+ Object.keys(value).some((key) => !['type', 'turn', ...(type === 'prompt' ? ['input'] : [])].includes(key)))
71
+ throw new OperationError('INVALID_INPUT', 'Invalid conversation command');
72
+ const reject = (code) => reply({ type: 'rejected', command: type, turn: value.turn, code });
73
+ if (type === 'prompt') {
74
+ if (active) {
75
+ await reject('BUSY');
76
+ continue;
77
+ }
78
+ if (value.turn !== turn + 1) {
79
+ await reject('STALE_TURN');
80
+ continue;
81
+ }
82
+ if (settledTurns >= maxTurns) {
83
+ await reject('TURN_LIMIT');
84
+ continue;
85
+ }
86
+ let prepared;
87
+ try {
88
+ // Initial context remains in the native conversation; these are new turn instructions.
89
+ prepared = prepareAgent(value.input);
90
+ if (prepared.session !== undefined)
91
+ throw new AgentMethodError('INVALID_INPUT', 'Session requests belong to the initial invocation');
92
+ }
93
+ catch (error) {
94
+ if (!(error instanceof AgentMethodError))
95
+ throw error;
96
+ await reject('INVALID_INPUT');
97
+ continue;
98
+ }
99
+ turn += 1;
100
+ interrupted = false;
101
+ active = start(prepared);
102
+ }
103
+ else if (type === 'interrupt') {
104
+ if (value.turn !== turn) {
105
+ await reject('STALE_TURN');
106
+ continue;
107
+ }
108
+ if (!active || interrupted) {
109
+ await reject('NOT_RUNNING');
110
+ continue;
111
+ }
112
+ if (!(await peer.interrupt())) {
113
+ await reject('NOT_RUNNING');
114
+ continue;
115
+ }
116
+ interrupted = true;
117
+ }
118
+ else {
119
+ if (active) {
120
+ await reject('BUSY');
121
+ continue;
122
+ }
123
+ if (value.turn !== turn) {
124
+ await reject('STALE_TURN');
125
+ continue;
126
+ }
127
+ await reply({ type: 'accepted', command: 'close', turn });
128
+ // Let the caller seal its writer before releasing this receiver. Merely
129
+ // cancelling our pending read would make a racing clean writer close fail.
130
+ const timeout = setTimeout(() => reads.abort(), 5_000);
131
+ try {
132
+ const end = await command;
133
+ signal.throwIfAborted();
134
+ if (reads.signal.aborted)
135
+ throw new OperationError('DEADLINE_EXCEEDED', 'Conversation command closure did not settle within five seconds');
136
+ if (end.kind === 'read-error')
137
+ throw end.error;
138
+ if (!end.item.done)
139
+ throw new OperationError('INVALID_INPUT', 'Conversation commands continued after accepted close');
140
+ }
141
+ finally {
142
+ clearTimeout(timeout);
143
+ }
144
+ return { outcome: 'done', output: { turns: settledTurns } };
145
+ }
146
+ await reply({ type: 'accepted', command: type, turn });
147
+ }
148
+ }
149
+ finally {
150
+ reads.abort();
151
+ await command;
152
+ // Every native prompt promise has a rejection handler. The enclosing owner
153
+ // cancels and settles the resource before disposing its endpoints on error.
154
+ }
155
+ }
package/dist/flow.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ import { type RunContext, type RunResult } from '@jigging/flow';
2
+ /** One replaceable method; process, credentials and reviewed policy stay outside. */
3
+ export declare function agentAcpFlow(run: RunContext): Promise<RunResult>;
4
+ export declare function runAgentAcpFlow(): Promise<void>;