@inhuman.tools/harness-sdk 0.0.0-stage → 0.2.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.
Files changed (123) hide show
  1. package/LICENSE.md +7 -0
  2. package/dist/client-agents-tree.d.ts +123 -0
  3. package/dist/client-agents-tree.d.ts.map +1 -0
  4. package/dist/client-agents-tree.js +504 -0
  5. package/dist/client-agents-tree.js.map +1 -0
  6. package/dist/client-agents.d.ts +50 -0
  7. package/dist/client-agents.d.ts.map +1 -0
  8. package/dist/client-agents.js +299 -0
  9. package/dist/client-agents.js.map +1 -0
  10. package/dist/client.d.ts +89 -0
  11. package/dist/client.d.ts.map +1 -0
  12. package/dist/client.js +589 -0
  13. package/dist/client.js.map +1 -0
  14. package/dist/index.d.ts +10 -0
  15. package/dist/index.d.ts.map +1 -0
  16. package/dist/index.js +10 -0
  17. package/dist/index.js.map +1 -0
  18. package/dist/launch/continue-pick.d.ts +17 -0
  19. package/dist/launch/continue-pick.d.ts.map +1 -0
  20. package/dist/launch/continue-pick.js +20 -0
  21. package/dist/launch/continue-pick.js.map +1 -0
  22. package/dist/launch/index.d.ts +6 -0
  23. package/dist/launch/index.d.ts.map +1 -0
  24. package/dist/launch/index.js +6 -0
  25. package/dist/launch/index.js.map +1 -0
  26. package/dist/launch/model-flags.d.ts +44 -0
  27. package/dist/launch/model-flags.d.ts.map +1 -0
  28. package/dist/launch/model-flags.js +216 -0
  29. package/dist/launch/model-flags.js.map +1 -0
  30. package/dist/launch/streams.d.ts +15 -0
  31. package/dist/launch/streams.d.ts.map +1 -0
  32. package/dist/launch/streams.js +2 -0
  33. package/dist/launch/streams.js.map +1 -0
  34. package/dist/launch/term-continue.d.ts +13 -0
  35. package/dist/launch/term-continue.d.ts.map +1 -0
  36. package/dist/launch/term-continue.js +17 -0
  37. package/dist/launch/term-continue.js.map +1 -0
  38. package/dist/launch/term-session-key.d.ts +8 -0
  39. package/dist/launch/term-session-key.d.ts.map +1 -0
  40. package/dist/launch/term-session-key.js +76 -0
  41. package/dist/launch/term-session-key.js.map +1 -0
  42. package/dist/observable-abort-signal.d.ts +7 -0
  43. package/dist/observable-abort-signal.d.ts.map +1 -0
  44. package/dist/observable-abort-signal.js +56 -0
  45. package/dist/observable-abort-signal.js.map +1 -0
  46. package/dist/open-external.d.ts +2 -0
  47. package/dist/open-external.d.ts.map +1 -0
  48. package/dist/open-external.js +24 -0
  49. package/dist/open-external.js.map +1 -0
  50. package/dist/paths.d.ts +7 -0
  51. package/dist/paths.d.ts.map +1 -0
  52. package/dist/paths.js +16 -0
  53. package/dist/paths.js.map +1 -0
  54. package/dist/text/agent-cover.d.ts +43 -0
  55. package/dist/text/agent-cover.d.ts.map +1 -0
  56. package/dist/text/agent-cover.js +123 -0
  57. package/dist/text/agent-cover.js.map +1 -0
  58. package/dist/text/format-shell.d.ts +5 -0
  59. package/dist/text/format-shell.d.ts.map +1 -0
  60. package/dist/text/format-shell.js +53 -0
  61. package/dist/text/format-shell.js.map +1 -0
  62. package/dist/text/identity.d.ts +67 -0
  63. package/dist/text/identity.d.ts.map +1 -0
  64. package/dist/text/identity.js +433 -0
  65. package/dist/text/identity.js.map +1 -0
  66. package/dist/text/index.d.ts +14 -0
  67. package/dist/text/index.d.ts.map +1 -0
  68. package/dist/text/index.js +15 -0
  69. package/dist/text/index.js.map +1 -0
  70. package/dist/text/liveness.d.ts +62 -0
  71. package/dist/text/liveness.d.ts.map +1 -0
  72. package/dist/text/liveness.js +270 -0
  73. package/dist/text/liveness.js.map +1 -0
  74. package/dist/text/mcp-identity.d.ts +25 -0
  75. package/dist/text/mcp-identity.d.ts.map +1 -0
  76. package/dist/text/mcp-identity.js +133 -0
  77. package/dist/text/mcp-identity.js.map +1 -0
  78. package/dist/text/pretty-identity.d.ts +15 -0
  79. package/dist/text/pretty-identity.d.ts.map +1 -0
  80. package/dist/text/pretty-identity.js +112 -0
  81. package/dist/text/pretty-identity.js.map +1 -0
  82. package/dist/text/sugarcoat.d.ts +29 -0
  83. package/dist/text/sugarcoat.d.ts.map +1 -0
  84. package/dist/text/sugarcoat.js +148 -0
  85. package/dist/text/sugarcoat.js.map +1 -0
  86. package/dist/text/unwrap-program.d.ts +2 -0
  87. package/dist/text/unwrap-program.d.ts.map +1 -0
  88. package/dist/text/unwrap-program.js +137 -0
  89. package/dist/text/unwrap-program.js.map +1 -0
  90. package/dist/text/work-text.d.ts +72 -0
  91. package/dist/text/work-text.d.ts.map +1 -0
  92. package/dist/text/work-text.js +337 -0
  93. package/dist/text/work-text.js.map +1 -0
  94. package/dist/transport/unix.d.ts +19 -0
  95. package/dist/transport/unix.d.ts.map +1 -0
  96. package/dist/transport/unix.js +68 -0
  97. package/dist/transport/unix.js.map +1 -0
  98. package/package.json +79 -3
  99. package/src/client-agents-tree.ts +642 -0
  100. package/src/client-agents.ts +230 -0
  101. package/src/client.ts +686 -0
  102. package/src/index.ts +27 -0
  103. package/src/launch/continue-pick.ts +28 -0
  104. package/src/launch/index.ts +5 -0
  105. package/src/launch/model-flags.ts +273 -0
  106. package/src/launch/streams.ts +13 -0
  107. package/src/launch/term-continue.ts +31 -0
  108. package/src/launch/term-session-key.ts +70 -0
  109. package/src/observable-abort-signal.ts +66 -0
  110. package/src/open-external.ts +22 -0
  111. package/src/paths.ts +20 -0
  112. package/src/text/agent-cover.ts +136 -0
  113. package/src/text/format-shell.ts +52 -0
  114. package/src/text/identity.ts +473 -0
  115. package/src/text/index.ts +29 -0
  116. package/src/text/liveness.ts +322 -0
  117. package/src/text/mcp-identity.ts +148 -0
  118. package/src/text/pretty-identity.ts +120 -0
  119. package/src/text/sugarcoat.ts +165 -0
  120. package/src/text/unwrap-program.ts +133 -0
  121. package/src/text/work-text.ts +388 -0
  122. package/src/transport/unix.ts +84 -0
  123. package/README.md +0 -3
@@ -0,0 +1,230 @@
1
+ /**
2
+ * Client projection of a worker session. Interned by `sessionId`.
3
+ * Reads the session overview + this tape + the three bots rosters. Does not intern the
4
+ * worker document.
5
+ */
6
+ import { recordString } from "@inhuman.tools/harness-model/json-record";
7
+ import type { SessionOverview } from "@inhuman.tools/harness-model/kernel";
8
+ import { AgentCallJob, BotCallJob, CallJob } from "@inhuman.tools/harness-model/session";
9
+ import { computed } from "mobx";
10
+
11
+ import type { Client } from "./client.js";
12
+
13
+ export type AgentScope = "one-shot" | "session" | "project" | "global";
14
+ export type AgentRosterScope = Exclude<AgentScope, "one-shot">;
15
+ /** Process. Failed is not this field — a sealed Call fail is `ourRequest`. */
16
+ export type AgentProcessState = "working" | "done";
17
+ /** Interaction. Needs-you is `blocked`, not `working`. */
18
+ export type AgentTempo = "active" | "idle" | "blocked";
19
+ /** This tape's Call to this worker. Cancel is not fail. */
20
+ export type AgentRequestState = "none" | "open" | "ok" | "fail" | "cancelled";
21
+
22
+ export type AgentRosterRow = {
23
+ readonly scope: AgentRosterScope;
24
+ readonly name: string;
25
+ readonly sessionId: string;
26
+ };
27
+
28
+ export class ClientAgent {
29
+ constructor(
30
+ readonly client: Client,
31
+ readonly sessionId: string,
32
+ ) {}
33
+
34
+ @computed
35
+ get overview(): SessionOverview | null {
36
+ return this.client.kernelRoot.sessions[this.sessionId] ?? null;
37
+ }
38
+
39
+ @computed
40
+ get names(): readonly string[] {
41
+ const names: string[] = [];
42
+ for (const row of this.client.botsRoster) {
43
+ if (row.sessionId === this.sessionId) names.push(row.name);
44
+ }
45
+ return names;
46
+ }
47
+
48
+ @computed
49
+ get scope(): AgentScope {
50
+ let session = false;
51
+ let project = false;
52
+ let global = false;
53
+ for (const row of this.client.botsRoster) {
54
+ if (row.sessionId !== this.sessionId) continue;
55
+ if (row.scope === "global") global = true;
56
+ else if (row.scope === "project") project = true;
57
+ else session = true;
58
+ }
59
+ if (global) return "global";
60
+ if (project) return "project";
61
+ if (session) return "session";
62
+ return "one-shot";
63
+ }
64
+
65
+ @computed
66
+ get oneShot(): boolean {
67
+ const glance = this.overview;
68
+ if (glance != null) return glance.oneShot;
69
+ const call = this.openCall ?? this.lastCall;
70
+ if (call != null) return call instanceof AgentCallJob;
71
+ return this.scope === "one-shot";
72
+ }
73
+
74
+ @computed
75
+ get specName(): string {
76
+ const fromGlance = this.overview?.specName.trim() ?? "";
77
+ if (fromGlance !== "") return fromGlance;
78
+ const named = this.names[0]?.trim() ?? "";
79
+ if (named !== "") return named;
80
+ const call = this.openCall ?? this.lastCall;
81
+ if (call == null) return "";
82
+ const spec = recordString(call.args, "spec");
83
+ return spec === "" && call instanceof BotCallJob ? call.botName.trim() || call.contributionBotName.trim() : spec;
84
+ }
85
+
86
+ @computed
87
+ get title(): string {
88
+ const title = this.overview?.title.trim() ?? "";
89
+ return title === "" ? this.specName : title;
90
+ }
91
+
92
+ @computed
93
+ get nature(): string {
94
+ const nature = this.overview?.recipeScope ?? "";
95
+ if (nature !== "") return nature;
96
+ const scope = this.scope;
97
+ return scope === "one-shot" ? "" : scope;
98
+ }
99
+
100
+ @computed
101
+ get keepAlive(): boolean {
102
+ return this.overview?.keepAlive === true;
103
+ }
104
+
105
+ @computed
106
+ get model(): string {
107
+ return this.overview?.model.trim() ?? "";
108
+ }
109
+
110
+ @computed
111
+ get status(): string {
112
+ return this.overview?.status.trim() ?? "";
113
+ }
114
+
115
+ @computed
116
+ get lastMessageAt(): number {
117
+ return this.overview?.lastMessageAt ?? 0;
118
+ }
119
+
120
+ @computed
121
+ get calls(): readonly CallJob[] {
122
+ const id = this.sessionId;
123
+ return this.client.agentCalls.filter((call) => call.workerSessionId === id);
124
+ }
125
+
126
+ @computed
127
+ get openCall(): CallJob | null {
128
+ let found: CallJob | null = null;
129
+ for (const call of this.calls) {
130
+ if (!call.completed) found = call;
131
+ }
132
+ return found;
133
+ }
134
+
135
+ @computed
136
+ get lastCall(): CallJob | null {
137
+ return this.calls.at(-1) ?? null;
138
+ }
139
+
140
+ @computed
141
+ get interacted(): boolean {
142
+ return this.calls.length > 0;
143
+ }
144
+
145
+ @computed
146
+ get ourRequest(): AgentRequestState {
147
+ const open = this.openCall;
148
+ if (open != null) return "open";
149
+ const last = this.lastCall;
150
+ if (last == null) return "none";
151
+ if (last.cancelled) return "cancelled";
152
+ const snap = last.outcomeSnap;
153
+ if (snap?.ok === false) return "fail";
154
+ return "ok";
155
+ }
156
+
157
+ @computed
158
+ get midMotion(): boolean {
159
+ return this.overview?.midMotion === true;
160
+ }
161
+
162
+ @computed
163
+ get openTurn(): boolean {
164
+ return this.overview?.openTurn === true;
165
+ }
166
+
167
+ @computed
168
+ get openToolCount(): number {
169
+ return this.overview?.openToolCount ?? 0;
170
+ }
171
+
172
+ @computed
173
+ get openCallbackCount(): number {
174
+ return this.overview?.openCallbackCount ?? 0;
175
+ }
176
+
177
+ @computed
178
+ get asking(): boolean {
179
+ return this.overview?.asking === true;
180
+ }
181
+
182
+ @computed
183
+ get state(): AgentProcessState {
184
+ if (
185
+ this.asking ||
186
+ this.midMotion ||
187
+ this.openTurn ||
188
+ this.openToolCount > 0 ||
189
+ this.openCallbackCount > 0 ||
190
+ this.openCall != null
191
+ )
192
+ return "working";
193
+ return "done";
194
+ }
195
+
196
+ @computed
197
+ get tempo(): AgentTempo {
198
+ if (this.asking) return "blocked";
199
+ if (
200
+ this.midMotion ||
201
+ this.openTurn ||
202
+ this.openToolCount > 0 ||
203
+ this.openCallbackCount > 0 ||
204
+ this.openCall != null
205
+ )
206
+ return "active";
207
+ return "idle";
208
+ }
209
+ }
210
+
211
+ /**
212
+ * One-shot and session-scope workers raise the host cover.
213
+ * Project/global confirm elsewhere. Host's own queue head wins.
214
+ */
215
+ export function hostAskEscalateSessionId(client: Client): string | null {
216
+ if (client.sessionRoot.findRunningTurn()?.approvalHead != null) return null;
217
+ for (const agent of client.agents) {
218
+ if (!escalatesAskToHost(agent)) continue;
219
+ if (!agent.asking && agent.overview?.unfinished !== "ask") continue;
220
+ return agent.sessionId;
221
+ }
222
+ return null;
223
+ }
224
+
225
+ function escalatesAskToHost(agent: ClientAgent): boolean {
226
+ if (agent.sessionId === "" || agent.sessionId === agent.client.sessionId) return false;
227
+ if (agent.scope === "project" || agent.scope === "global") return false;
228
+ if (agent.nature === "project" || agent.nature === "global") return false;
229
+ return agent.oneShot || agent.scope === "session" || agent.nature === "session";
230
+ }