@agentshouse/kit 0.2.1-c7f53ff9eaf98 → 0.2.1-c84a4fccf2835

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.
@@ -1,23 +1,23 @@
1
1
  {
2
2
  "schemaVersion": 2,
3
- "version": "0.2.1-c7f53ff9eaf98",
3
+ "version": "0.2.1-c84a4fccf2835",
4
4
  "hosts": {
5
5
  "macos": {
6
6
  "asset": {
7
7
  "name": "connect-linux.sh",
8
- "url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c7f53ff9eaf98/connect-linux.sh"
8
+ "url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c84a4fccf2835/connect-linux.sh"
9
9
  }
10
10
  },
11
11
  "linux": {
12
12
  "asset": {
13
13
  "name": "connect-linux.sh",
14
- "url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c7f53ff9eaf98/connect-linux.sh"
14
+ "url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c84a4fccf2835/connect-linux.sh"
15
15
  }
16
16
  },
17
17
  "windows": {
18
18
  "asset": {
19
19
  "name": "connect-windows.ps1",
20
- "url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c7f53ff9eaf98/connect-windows.ps1"
20
+ "url": "https://github.com/agentshouse/kit/releases/download/v0.2.1-c84a4fccf2835/connect-windows.ps1"
21
21
  }
22
22
  }
23
23
  }
package/dist/bridge.js CHANGED
@@ -81,7 +81,7 @@ function editAnswer(forwarded) {
81
81
  export async function openBridge(house, copies, conversationId, signal) {
82
82
  const path = [join(kitHome(), 'shim'), dirname(process.execPath), await loginPath()].join(delimiter);
83
83
  const origin = (await readEnrolment()).house;
84
- const { credential } = await house.post(`/kit/conversations/${conversationId}/credential`, {}, signal);
84
+ const { credential } = await retrying(() => house.post(`/kit/conversations/${conversationId}/credential`, {}, signal), signal);
85
85
  const closed = new AbortController();
86
86
  const sent = (message, operation) => callHouse(origin, credential, message, operation, closed.signal);
87
87
  const send = async (message, operation) => {
@@ -4,6 +4,7 @@ import { mkdir } from 'node:fs/promises';
4
4
  import { join } from 'node:path';
5
5
  import { unlessAborted } from './abort.js';
6
6
  import { startAdapter } from './acp.js';
7
+ import { finalRefusal } from './api.js';
7
8
  import { openBridge } from './bridge.js';
8
9
  import { CLIS } from './clis.js';
9
10
  import { answeredFailure, readerFor } from './events.js';
@@ -11,6 +12,7 @@ import { placeFiles, uploadBytes } from './files.js';
11
12
  import { agentBase } from './home.js';
12
13
  import { instructions } from './instructions.js';
13
14
  import { Parts } from './parts.js';
15
+ import { refusalOf } from './refusals.js';
14
16
  import { killMarked, Scope } from './scope.js';
15
17
  import { holdSecretInput } from './secret-input.js';
16
18
  import { createHowWeWork } from './skills.js';
@@ -19,7 +21,13 @@ import { workFor } from './work.js';
19
21
  const SIGNED_OUT = RequestError.authRequired().code;
20
22
  const KILLED = 'the conversation was killed';
21
23
  const REFUSED = 'the CLI refused the prompt';
22
- const NO_CONNECTION = "the Kit could not open this conversation's connection";
24
+ const NO_CONNECTION = "the Agent's computer could not open this conversation; try again";
25
+ const GONE = 'House no longer holds this conversation; start a new one';
26
+ const RETIRED = "this conversation's Agent is retired, so it cannot start";
27
+ const ENDED = {
28
+ conversation_not_found: GONE,
29
+ conversation_historical: RETIRED,
30
+ };
23
31
  const NO_START = "the Agent's CLI did not start";
24
32
  const NO_SESSION = "the Agent's CLI did not open a session";
25
33
  const NO_RESUME = "the Agent's CLI could not resume this conversation";
@@ -31,9 +39,12 @@ async function step(reason, work) {
31
39
  return await work;
32
40
  }
33
41
  catch (error) {
34
- throw new NotStarted(reason, { cause: error });
42
+ throw new NotStarted(typeof reason === 'string' ? reason : reason(error), { cause: error });
35
43
  }
36
44
  }
45
+ function unbridged(error) {
46
+ return (finalRefusal(error) ? ENDED[refusalOf(error.text)?.code ?? ''] : undefined) ?? NO_CONNECTION;
47
+ }
37
48
  const EXIT_PLAN_MODE = {
38
49
  parse(value) {
39
50
  if (typeof value !== 'object' || value === null || Array.isArray(value)) {
@@ -119,9 +130,7 @@ function asksSecret(request) {
119
130
  export class Conversations {
120
131
  kit;
121
132
  conversations = new Map();
122
- acks = new Map();
123
133
  questions = new Map();
124
- carrying = 0;
125
134
  reporting = 0;
126
135
  watched = false;
127
136
  constructor(kit) {
@@ -130,37 +139,26 @@ export class Conversations {
130
139
  setInterval(() => this.look(), 2_000);
131
140
  }
132
141
  input(input) {
133
- this.carrying++;
134
- const carried = this.acks.get(input.input_id) ??
135
- (() => {
136
- const conversation = this.conversation(input.conversation_id);
137
- if (input.kind === 'kill') {
138
- conversation.kills++;
139
- conversation.killed.abort(new Error(KILLED));
140
- for (const waiting of conversation.waiting.splice(0))
141
- waiting.settle({ refused: KILLED });
142
- this.halt(conversation);
143
- }
144
- if (input.kind === 'interrupt') {
145
- conversation.interrupts++;
146
- conversation.interrupted.abort();
147
- }
148
- const handled = conversation.queue.then(() => this.carry(conversation, input));
149
- conversation.queue = handled.catch(() => undefined);
150
- const work = handled.then(({ ack }) => ack);
151
- this.acks.set(input.input_id, work);
152
- return work;
153
- })();
154
- carried
155
- .then((body) => this.kit.house.deliver(`/kit/inputs/${input.input_id}/ack`, body))
156
- .catch(logged)
157
- .finally(() => {
158
- this.carrying--;
159
- this.kit.changed();
142
+ void this.kit.inputs.carry(input.input_id, () => {
143
+ const conversation = this.conversation(input.conversation_id);
144
+ if (input.kind === 'kill') {
145
+ conversation.kills++;
146
+ conversation.killed.abort(new Error(KILLED));
147
+ for (const waiting of conversation.waiting.splice(0))
148
+ waiting.settle({ refused: KILLED });
149
+ this.halt(conversation);
150
+ }
151
+ if (input.kind === 'interrupt') {
152
+ conversation.interrupts++;
153
+ conversation.interrupted.abort();
154
+ }
155
+ const handled = conversation.queue.then(() => this.carry(conversation, input));
156
+ conversation.queue = handled.catch(() => undefined);
157
+ return handled.then(async ({ ack }) => ({ ack: await ack }));
160
158
  });
161
159
  }
162
160
  idle() {
163
- if (this.carrying > 0 || this.reporting > 0)
161
+ if (this.reporting > 0)
164
162
  return false;
165
163
  for (const conversation of this.conversations.values()) {
166
164
  const turn = conversation.turn;
@@ -395,7 +393,7 @@ export class Conversations {
395
393
  .onRequest('elicitation/create', ({ params, signal }) => this.ask(conversation, 'elicitation/create', params, asksSecret(params), signal))
396
394
  .onRequest('_x.ai/exit_plan_mode', EXIT_PLAN_MODE, ({ params, signal }) => this.ask(conversation, '_x.ai/exit_plan_mode', params, false, signal));
397
395
  const cli = await unlessAborted(this.kit.agents.cli(route.kind), conversation.killed.signal);
398
- const bridge = await step(NO_CONNECTION, openBridge(this.kit.house, this.kit.copies, conversation.id, conversation.killed.signal));
396
+ const bridge = await step(unbridged, openBridge(this.kit.house, this.kit.copies, conversation.id, conversation.killed.signal));
399
397
  if (conversation.kills > 0) {
400
398
  bridge.close();
401
399
  throw new Error(KILLED);
package/dist/files.js CHANGED
@@ -11,7 +11,7 @@ export function verified(bytes, sha256, name) {
11
11
  export async function placeFiles(house, directory, files, signal) {
12
12
  const paths = [];
13
13
  for (const file of files) {
14
- const original = await house.post('/kit/conversation/originals/get', { version: file.version, download: true }, signal);
14
+ const original = await retrying(() => house.post('/kit/conversation/originals/get', { version: file.version, download: true }, signal), signal);
15
15
  const answer = await fetch(relayed(original.download.url), { signal });
16
16
  if (!answer.ok)
17
17
  throw new Error(`${file.name} did not download: ${answer.status}`);
package/dist/inputs.js ADDED
@@ -0,0 +1,37 @@
1
+ function logged(error) {
2
+ process.stderr.write(`kit: ${error instanceof Error ? error.message : String(error)}\n`);
3
+ }
4
+ export class Inputs {
5
+ house;
6
+ changed;
7
+ carried = new Map();
8
+ open = 0;
9
+ constructor(house, changed) {
10
+ this.house = house;
11
+ this.changed = changed;
12
+ }
13
+ idle() {
14
+ return this.open === 0;
15
+ }
16
+ carry(inputId, work) {
17
+ const known = this.carried.get(inputId);
18
+ const carried = known ?? work();
19
+ if (known === undefined) {
20
+ this.carried.set(inputId, carried);
21
+ carried.catch(() => this.carried.delete(inputId));
22
+ }
23
+ this.open++;
24
+ this.changed();
25
+ return carried
26
+ .then(async ({ ack, acknowledged }) => {
27
+ await this.house.deliver(`/kit/inputs/${inputId}/ack`, ack);
28
+ if (known === undefined)
29
+ await acknowledged?.();
30
+ })
31
+ .catch(logged)
32
+ .finally(() => {
33
+ this.open--;
34
+ this.changed();
35
+ });
36
+ }
37
+ }
package/dist/resident.js CHANGED
@@ -12,6 +12,7 @@ import { CONVERSATION_INPUTS, Conversations } from './conversations.js';
12
12
  import { LocalCopies } from './copies.js';
13
13
  import { restart } from './end.js';
14
14
  import { agentBase, enrolled, forgetRestart, kitHome, ownerLock, ownerSocket, restartAsked } from './home.js';
15
+ import { Inputs } from './inputs.js';
15
16
  import { killDescendants } from './scope.js';
16
17
  import { quoted } from './shell.js';
17
18
  import { SignIns } from './sign-in.js';
@@ -133,7 +134,7 @@ export async function resident() {
133
134
  const agents = new Agents(house, () => changed());
134
135
  let stream = null;
135
136
  let reported = false;
136
- const idle = () => conversations.idle() && signIns.idle() && agents.idle();
137
+ const idle = () => inputs.idle() && conversations.idle() && agents.idle();
137
138
  const changed = () => {
138
139
  if (!idle()) {
139
140
  reported = false;
@@ -146,15 +147,17 @@ export async function resident() {
146
147
  logged(house.deliver('/kit/idle', {}, idle));
147
148
  }
148
149
  };
150
+ const inputs = new Inputs(house, changed);
149
151
  const conversations = new Conversations({
150
152
  house,
151
153
  agents,
152
154
  copies,
155
+ inputs,
153
156
  send: (frame) => stream?.send(frame) ?? false,
154
157
  changed,
155
158
  });
156
- const signIns = new SignIns(house, agents, changed);
157
- const updates = new Updates(house);
159
+ const signIns = new SignIns(house, agents, inputs);
160
+ const updates = new Updates(inputs);
158
161
  for (const signal of ['SIGTERM', 'SIGINT']) {
159
162
  process.once(signal, () => {
160
163
  killDescendants();
@@ -178,7 +181,6 @@ export async function resident() {
178
181
  if (arrived.type === 'work_available' && arrived.subject === 'agents')
179
182
  logged(agents.refresh());
180
183
  if (arrived.type === 'input') {
181
- reported = false;
182
184
  if (arrived.kind === 'sign_in')
183
185
  signIns.start(arrived);
184
186
  else if (arrived.kind === 'update')
@@ -187,7 +189,7 @@ export async function resident() {
187
189
  conversations.input(arrived);
188
190
  else {
189
191
  const refused = { refused: `This Kit does not know ${arrived.kind}; update the Kit` };
190
- logged(house.deliver(`/kit/inputs/${arrived.input_id}/ack`, refused).finally(changed));
192
+ void inputs.carry(String(arrived.input_id), async () => ({ ack: refused }));
191
193
  }
192
194
  }
193
195
  },
package/dist/sign-in.js CHANGED
@@ -9,36 +9,20 @@ const CODE = /^[A-Z0-9]+-[A-Z0-9]+$/;
9
9
  function causeOf(error) {
10
10
  return error instanceof Error ? error.message : String(error);
11
11
  }
12
- function logged(error) {
13
- process.stderr.write(`kit: ${causeOf(error)}\n`);
14
- }
15
12
  function endOf(signal) {
16
13
  return new Promise((resolve) => signal.addEventListener('abort', () => resolve(true), { once: true }));
17
14
  }
18
15
  export class SignIns {
19
16
  house;
20
17
  agents;
21
- changed;
22
- running = new Set();
23
- constructor(house, agents, changed) {
18
+ inputs;
19
+ constructor(house, agents, inputs) {
24
20
  this.house = house;
25
21
  this.agents = agents;
26
- this.changed = changed;
27
- }
28
- idle() {
29
- return this.running.size === 0;
22
+ this.inputs = inputs;
30
23
  }
31
24
  start(input) {
32
- if (this.running.has(input.input_id))
33
- return;
34
- this.running.add(input.input_id);
35
- this.changed();
36
- this.signIn(input)
37
- .catch(logged)
38
- .finally(() => {
39
- this.running.delete(input.input_id);
40
- this.changed();
41
- });
25
+ void this.inputs.carry(input.input_id, () => this.signIn(input));
42
26
  }
43
27
  async signIn(input) {
44
28
  const ending = new AbortController();
@@ -48,9 +32,7 @@ export class SignIns {
48
32
  clearTimeout(timer);
49
33
  if (ended)
50
34
  process.stderr.write(`kit: the ${input.cli} sign-in reached the end House set for it\n`);
51
- await this.house.deliver(`/kit/inputs/${input.input_id}/ack`, {});
52
- if (!ended)
53
- await this.agents.reread(input.cli);
35
+ return ended ? { ack: {} } : { ack: {}, acknowledged: () => this.agents.reread(input.cli) };
54
36
  }
55
37
  async signedIn(input, signal) {
56
38
  await this.login(input, signal);
package/dist/update.js CHANGED
@@ -6,9 +6,6 @@ import { join } from 'node:path';
6
6
  import { run } from './clis.js';
7
7
  import { kitHome, workspaceRoot } from './home.js';
8
8
  import { service, throughSudo } from './placement.js';
9
- function logged(error) {
10
- process.stderr.write(`kit: ${error instanceof Error ? error.message : String(error)}\n`);
11
- }
12
9
  function directory() {
13
10
  return join(kitHome(), 'update');
14
11
  }
@@ -50,6 +47,12 @@ async function recorded() {
50
47
  return null;
51
48
  return status.trim() === '0' ? {} : { refused: `the Kit bootstrap exited with ${status.trim()}` };
52
49
  }
50
+ async function forget(inputId) {
51
+ if ((await pendingInput()) !== inputId)
52
+ return;
53
+ await rm(pending(), { force: true });
54
+ await rm(outcome(), { force: true });
55
+ }
53
56
  async function finished(inputId) {
54
57
  for (;;) {
55
58
  const ack = await recorded();
@@ -90,27 +93,28 @@ function rerun(script, inputId, flags) {
90
93
  });
91
94
  }
92
95
  export class Updates {
93
- house;
94
- held = new Set();
96
+ inputs;
95
97
  queue = Promise.resolve();
96
- constructor(house) {
97
- this.house = house;
98
+ constructor(inputs) {
99
+ this.inputs = inputs;
98
100
  }
99
101
  async resumed() {
100
102
  const inputId = await pendingInput();
101
103
  if (inputId === null)
102
104
  return;
103
- await this.acknowledge(inputId, await finished(inputId));
105
+ await this.enqueue(inputId, () => finished(inputId));
104
106
  }
105
107
  start(input) {
106
- if (this.held.has(input.input_id))
107
- return;
108
- this.held.add(input.input_id);
109
- this.queue = this.queue
110
- .then(() => this.carry(input))
111
- .then((ack) => this.acknowledge(input.input_id, ack))
112
- .catch(logged)
113
- .finally(() => this.held.delete(input.input_id));
108
+ void this.enqueue(input.input_id, () => this.carry(input));
109
+ }
110
+ enqueue(inputId, work) {
111
+ const before = this.queue;
112
+ const carried = this.inputs.carry(inputId, async () => {
113
+ await before;
114
+ return { ack: await work(), acknowledged: () => forget(inputId) };
115
+ });
116
+ this.queue = before.then(() => carried);
117
+ return carried;
114
118
  }
115
119
  async carry(input) {
116
120
  if ((await pendingInput()) === input.input_id)
@@ -131,11 +135,4 @@ export class Updates {
131
135
  refused: 'the Kit bootstrap did not finish',
132
136
  };
133
137
  }
134
- async acknowledge(inputId, ack) {
135
- await this.house.deliver(`/kit/inputs/${inputId}/ack`, ack);
136
- if ((await pendingInput()) !== inputId)
137
- return;
138
- await rm(pending(), { force: true });
139
- await rm(outcome(), { force: true });
140
- }
141
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentshouse/kit",
3
- "version": "0.2.1-c7f53ff9eaf98",
3
+ "version": "0.2.1-c84a4fccf2835",
4
4
  "description": "House Kit connects an Environment to agents.house.",
5
5
  "license": "MIT",
6
6
  "repository": {