laohuang 0.3.2 → 0.4.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 (86) hide show
  1. package/README.md +142 -16
  2. package/dist/agent.d.ts +166 -0
  3. package/dist/agent.js +858 -0
  4. package/dist/agent.js.map +1 -0
  5. package/dist/bash-runner.d.ts +79 -0
  6. package/dist/bash-runner.js +464 -0
  7. package/dist/bash-runner.js.map +1 -0
  8. package/dist/cancellation.d.ts +36 -0
  9. package/dist/cancellation.js +123 -0
  10. package/dist/cancellation.js.map +1 -0
  11. package/dist/cli.d.ts +117 -0
  12. package/dist/cli.js +1307 -0
  13. package/dist/cli.js.map +1 -0
  14. package/dist/client.d.ts +21 -0
  15. package/dist/client.js +17 -0
  16. package/dist/client.js.map +1 -0
  17. package/dist/commands.d.ts +123 -0
  18. package/dist/commands.js +660 -0
  19. package/dist/commands.js.map +1 -0
  20. package/dist/config.d.ts +51 -0
  21. package/dist/config.js +183 -0
  22. package/dist/config.js.map +1 -0
  23. package/dist/credentials.d.ts +12 -0
  24. package/dist/credentials.js +102 -0
  25. package/dist/credentials.js.map +1 -0
  26. package/dist/events.d.ts +288 -0
  27. package/dist/events.js +838 -0
  28. package/dist/events.js.map +1 -0
  29. package/dist/model-adapter.d.ts +138 -0
  30. package/dist/model-adapter.js +244 -0
  31. package/dist/model-adapter.js.map +1 -0
  32. package/dist/model-selection.d.ts +67 -0
  33. package/dist/model-selection.js +148 -0
  34. package/dist/model-selection.js.map +1 -0
  35. package/dist/model-stream.d.ts +128 -0
  36. package/dist/model-stream.js +582 -0
  37. package/dist/model-stream.js.map +1 -0
  38. package/dist/project-instructions.d.ts +99 -0
  39. package/dist/project-instructions.js +348 -0
  40. package/dist/project-instructions.js.map +1 -0
  41. package/dist/providers.d.ts +9 -0
  42. package/dist/providers.js +26 -0
  43. package/dist/providers.js.map +1 -0
  44. package/dist/routing.d.ts +170 -0
  45. package/dist/routing.js +669 -0
  46. package/dist/routing.js.map +1 -0
  47. package/dist/semantic-classifier.d.ts +66 -0
  48. package/dist/semantic-classifier.js +86 -0
  49. package/dist/semantic-classifier.js.map +1 -0
  50. package/dist/session.d.ts +162 -0
  51. package/dist/session.js +871 -0
  52. package/dist/session.js.map +1 -0
  53. package/dist/system-prompt.d.ts +16 -0
  54. package/dist/system-prompt.js +42 -0
  55. package/dist/system-prompt.js.map +1 -0
  56. package/dist/terminal/editor.d.ts +161 -0
  57. package/dist/terminal/editor.js +1060 -0
  58. package/dist/terminal/editor.js.map +1 -0
  59. package/dist/terminal/input.d.ts +61 -0
  60. package/dist/terminal/input.js +276 -0
  61. package/dist/terminal/input.js.map +1 -0
  62. package/dist/terminal/markdown.d.ts +20 -0
  63. package/dist/terminal/markdown.js +621 -0
  64. package/dist/terminal/markdown.js.map +1 -0
  65. package/dist/terminal/screen.d.ts +66 -0
  66. package/dist/terminal/screen.js +624 -0
  67. package/dist/terminal/screen.js.map +1 -0
  68. package/dist/terminal/theme.d.ts +23 -0
  69. package/dist/terminal/theme.js +101 -0
  70. package/dist/terminal/theme.js.map +1 -0
  71. package/dist/terminal/ui.d.ts +285 -0
  72. package/dist/terminal/ui.js +1815 -0
  73. package/dist/terminal/ui.js.map +1 -0
  74. package/dist/tools.d.ts +95 -0
  75. package/dist/tools.js +444 -0
  76. package/dist/tools.js.map +1 -0
  77. package/dist/ui-state.d.ts +51 -0
  78. package/dist/ui-state.js +194 -0
  79. package/dist/ui-state.js.map +1 -0
  80. package/dist/web.d.ts +56 -0
  81. package/dist/web.js +247 -0
  82. package/dist/web.js.map +1 -0
  83. package/package.json +24 -21
  84. package/bin/laohuang.js +0 -10
  85. package/lib/launcher.js +0 -133
  86. package/vendor/laohuangcode-0.3.2-py3-none-any.whl +0 -0
@@ -0,0 +1,194 @@
1
+ /** Event-reduced terminal state, independent from the agent runtime. */
2
+ export function createUIState() {
3
+ return {
4
+ sessionState: "IDLE",
5
+ activeResponse: null,
6
+ activeTools: new Map(),
7
+ pendingCount: 0,
8
+ heldCount: 0,
9
+ provider: "",
10
+ model: "",
11
+ totalTokens: 0,
12
+ inputTokens: 0,
13
+ outputTokens: 0,
14
+ };
15
+ }
16
+ function createUpdate(kind, fields = {}) {
17
+ return {
18
+ kind,
19
+ text: fields.text ?? "",
20
+ correlationId: fields.correlationId ?? "",
21
+ stream: fields.stream ?? "",
22
+ payload: fields.payload ?? {},
23
+ };
24
+ }
25
+ function isRecord(value) {
26
+ return typeof value === "object" && value !== null && !Array.isArray(value);
27
+ }
28
+ /** Mirror Python's `getattr(raw, "value", raw)` enum unwrapping. */
29
+ function unwrapValue(raw) {
30
+ return isRecord(raw) && "value" in raw ? raw.value : raw;
31
+ }
32
+ /** Mirror Python's `int(...)`: truncate numbers, parse numeric strings. */
33
+ function toInt(value) {
34
+ const parsed = typeof value === "string" ? Number.parseInt(value, 10) : Number(value);
35
+ return Number.isFinite(parsed) ? Math.trunc(parsed) : 0;
36
+ }
37
+ /** Turn canonical events or projected dictionaries into small UI updates. */
38
+ export class UIEventReducer {
39
+ static #TOOL_BUFFER_LIMIT = 20_000;
40
+ state;
41
+ constructor(state) {
42
+ this.state = state ?? createUIState();
43
+ }
44
+ apply(event) {
45
+ const kind = UIEventReducer.#kindOf(event);
46
+ const payload = UIEventReducer.#payloadOf(event);
47
+ const correlationId = UIEventReducer.#correlationIdOf(event);
48
+ if (kind === "ui.message") {
49
+ return createUpdate(kind, {
50
+ text: String(payload.text ?? ""),
51
+ payload,
52
+ });
53
+ }
54
+ if (kind === "task.state_changed") {
55
+ const rawState = payload.state ?? "IDLE";
56
+ this.state.sessionState = String(unwrapValue(rawState)).toUpperCase();
57
+ this.#updateCounts(payload);
58
+ return createUpdate(kind, { payload });
59
+ }
60
+ if (kind === "input.pending" || kind === "input.held") {
61
+ this.#updateCounts(payload);
62
+ return createUpdate(kind, { payload });
63
+ }
64
+ if (kind === "task.started") {
65
+ this.state.sessionState = "RUNNING_MODEL";
66
+ this.#updateCounts(payload);
67
+ return createUpdate(kind, { payload });
68
+ }
69
+ if (kind === "task.completed" ||
70
+ kind === "task.cancelled" ||
71
+ kind === "task.failed") {
72
+ this.state.sessionState = kind === "task.failed" ? "FAILED" : "IDLE";
73
+ this.#updateCounts(payload);
74
+ return createUpdate(kind, { payload });
75
+ }
76
+ if (kind === "model.request_started") {
77
+ this.state.sessionState = "RUNNING_MODEL";
78
+ this.state.activeResponse = {
79
+ requestId: correlationId,
80
+ text: "",
81
+ status: "provisional",
82
+ };
83
+ return createUpdate(kind, { correlationId, payload });
84
+ }
85
+ if (kind === "model.text_delta") {
86
+ const text = String(payload.text ?? payload.chunk ?? "");
87
+ let response = this.state.activeResponse;
88
+ if (response === null || response.requestId !== correlationId) {
89
+ response = { requestId: correlationId, text: "", status: "provisional" };
90
+ this.state.activeResponse = response;
91
+ }
92
+ response.text += text;
93
+ return createUpdate(kind, { text, correlationId });
94
+ }
95
+ if (kind === "model.response_committed") {
96
+ if (this.state.activeResponse !== null) {
97
+ this.state.activeResponse.status = "committed";
98
+ }
99
+ return createUpdate(kind, { correlationId, payload });
100
+ }
101
+ if (kind === "model.response_aborted" || kind === "model.request_failed") {
102
+ if (this.state.activeResponse !== null) {
103
+ this.state.activeResponse.status = "aborted";
104
+ }
105
+ return createUpdate(kind, { correlationId, payload });
106
+ }
107
+ if (kind === "model.response_summary") {
108
+ const usage = payload.usage;
109
+ if (isRecord(usage)) {
110
+ this.state.inputTokens += toInt(("prompt_tokens" in usage ? usage.prompt_tokens : usage.input_tokens) || 0);
111
+ this.state.outputTokens += toInt(("completion_tokens" in usage
112
+ ? usage.completion_tokens
113
+ : usage.output_tokens) || 0);
114
+ }
115
+ this.state.totalTokens = toInt(("total_tokens" in payload ? payload.total_tokens : this.state.totalTokens) ||
116
+ 0);
117
+ return createUpdate(kind, { correlationId, payload });
118
+ }
119
+ if (kind === "model.reasoning_delta") {
120
+ return createUpdate(kind, {
121
+ text: String(payload.text ?? ""),
122
+ correlationId,
123
+ payload,
124
+ });
125
+ }
126
+ if (kind === "tool.started") {
127
+ const name = String(payload.name ?? "tool");
128
+ const args = payload.arguments;
129
+ let subject = "";
130
+ if (isRecord(args)) {
131
+ subject = String(args.command || args.path || "");
132
+ }
133
+ this.state.activeTools.set(correlationId, {
134
+ toolCallId: correlationId,
135
+ name,
136
+ subject,
137
+ stdout: "",
138
+ stderr: "",
139
+ status: "running",
140
+ });
141
+ this.state.sessionState = "RUNNING_TOOLS";
142
+ return createUpdate(kind, { correlationId, payload });
143
+ }
144
+ if (kind === "tool.output_delta") {
145
+ const stream = String(payload.stream ?? "stdout");
146
+ if (stream === "stdout") {
147
+ // Stdout remains available to the model in the tool result,
148
+ // but it is intentionally absent from user-facing UI state.
149
+ return null;
150
+ }
151
+ const text = String(payload.text ?? payload.chunk ?? "");
152
+ const tool = this.state.activeTools.get(correlationId);
153
+ if (tool !== undefined) {
154
+ tool.stderr = (tool.stderr + text).slice(-UIEventReducer.#TOOL_BUFFER_LIMIT);
155
+ }
156
+ return createUpdate(kind, { text, correlationId, stream });
157
+ }
158
+ if (kind === "tool.finished") {
159
+ const tool = this.state.activeTools.get(correlationId);
160
+ this.state.activeTools.delete(correlationId);
161
+ if (tool !== undefined) {
162
+ tool.status = String(payload.status ?? "completed");
163
+ }
164
+ return createUpdate(kind, { correlationId, payload });
165
+ }
166
+ if (kind === "model.switched") {
167
+ this.state.provider = String("provider" in payload ? payload.provider : this.state.provider);
168
+ this.state.model = String("model" in payload ? payload.model : this.state.model);
169
+ return createUpdate(kind, { payload });
170
+ }
171
+ return null;
172
+ }
173
+ #updateCounts(payload) {
174
+ if ("pending_count" in payload) {
175
+ this.state.pendingCount = toInt(payload.pending_count);
176
+ }
177
+ if ("held_count" in payload) {
178
+ this.state.heldCount = toInt(payload.held_count);
179
+ }
180
+ }
181
+ static #kindOf(event) {
182
+ const raw = "kind" in event ? event.kind : event.type ?? "";
183
+ return String(unwrapValue(raw) ?? "");
184
+ }
185
+ static #payloadOf(event) {
186
+ const payload = "payload" in event ? event.payload : undefined;
187
+ return isRecord(payload) ? payload : {};
188
+ }
189
+ static #correlationIdOf(event) {
190
+ const raw = "correlation_id" in event ? event.correlation_id : "";
191
+ return raw ? String(raw) : "";
192
+ }
193
+ }
194
+ //# sourceMappingURL=ui-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui-state.js","sourceRoot":"","sources":["../src/ui-state.ts"],"names":[],"mappings":"AAAA,wEAAwE;AA8BxE,MAAM,UAAU,aAAa;IAC3B,OAAO;QACL,YAAY,EAAE,MAAM;QACpB,cAAc,EAAE,IAAI;QACpB,WAAW,EAAE,IAAI,GAAG,EAAE;QACtB,YAAY,EAAE,CAAC;QACf,SAAS,EAAE,CAAC;QACZ,QAAQ,EAAE,EAAE;QACZ,KAAK,EAAE,EAAE;QACT,WAAW,EAAE,CAAC;QACd,WAAW,EAAE,CAAC;QACd,YAAY,EAAE,CAAC;KAChB,CAAC;AACJ,CAAC;AAUD,SAAS,YAAY,CACnB,IAAY,EACZ,SAA0C,EAAE;IAE5C,OAAO;QACL,IAAI;QACJ,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;QACvB,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,EAAE;QACzC,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE;KAC9B,CAAC;AACJ,CAAC;AAaD,SAAS,QAAQ,CAAC,KAAc;IAC9B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAC9E,CAAC;AAED,oEAAoE;AACpE,SAAS,WAAW,CAAC,GAAY;IAC/B,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,OAAO,IAAI,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC;AAC3D,CAAC;AAED,2EAA2E;AAC3E,SAAS,KAAK,CAAC,KAAc;IAC3B,MAAM,MAAM,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACtF,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,6EAA6E;AAC7E,MAAM,OAAO,cAAc;IACzB,MAAM,CAAU,kBAAkB,GAAG,MAAM,CAAC;IAEnC,KAAK,CAAU;IAExB,YAAY,KAAe;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,IAAI,aAAa,EAAE,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,KAAkB;QACtB,MAAM,IAAI,GAAG,cAAc,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACjD,MAAM,aAAa,GAAG,cAAc,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAAC;QAE7D,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YAC1B,OAAO,YAAY,CAAC,IAAI,EAAE;gBACxB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;gBAChC,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,KAAK,oBAAoB,EAAE,CAAC;YAClC,MAAM,QAAQ,GAAG,OAAO,CAAC,KAAK,IAAI,MAAM,CAAC;YACzC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,MAAM,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;YACtE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,IAAI,KAAK,eAAe,IAAI,IAAI,KAAK,YAAY,EAAE,CAAC;YACtD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,eAAe,CAAC;YAC1C,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QACD,IACE,IAAI,KAAK,gBAAgB;YACzB,IAAI,KAAK,gBAAgB;YACzB,IAAI,KAAK,aAAa,EACtB,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,IAAI,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC;YACrE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;YAC5B,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACrC,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,eAAe,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG;gBAC1B,SAAS,EAAE,aAAa;gBACxB,IAAI,EAAE,EAAE;gBACR,MAAM,EAAE,aAAa;aACtB,CAAC;YACF,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,KAAK,kBAAkB,EAAE,CAAC;YAChC,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YACzD,IAAI,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC;YACzC,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,SAAS,KAAK,aAAa,EAAE,CAAC;gBAC9D,QAAQ,GAAG,EAAE,SAAS,EAAE,aAAa,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,aAAa,EAAE,CAAC;gBACzE,IAAI,CAAC,KAAK,CAAC,cAAc,GAAG,QAAQ,CAAC;YACvC,CAAC;YACD,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC;YACtB,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,CAAC;QACrD,CAAC;QACD,IAAI,IAAI,KAAK,0BAA0B,EAAE,CAAC;YACxC,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;gBACvC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,WAAW,CAAC;YACjD,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,KAAK,wBAAwB,IAAI,IAAI,KAAK,sBAAsB,EAAE,CAAC;YACzE,IAAI,IAAI,CAAC,KAAK,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;gBACvC,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,MAAM,GAAG,SAAS,CAAC;YAC/C,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,KAAK,wBAAwB,EAAE,CAAC;YACtC,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;YAC5B,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBACpB,IAAI,CAAC,KAAK,CAAC,WAAW,IAAI,KAAK,CAC7B,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,CAC3E,CAAC;gBACF,IAAI,CAAC,KAAK,CAAC,YAAY,IAAI,KAAK,CAC9B,CAAC,mBAAmB,IAAI,KAAK;oBAC3B,CAAC,CAAC,KAAK,CAAC,iBAAiB;oBACzB,CAAC,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAC9B,CAAC;YACJ,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,WAAW,GAAG,KAAK,CAC5B,CAAC,cAAc,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC;gBACzE,CAAC,CACJ,CAAC;YACF,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,KAAK,uBAAuB,EAAE,CAAC;YACrC,OAAO,YAAY,CAAC,IAAI,EAAE;gBACxB,IAAI,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,EAAE,CAAC;gBAChC,aAAa;gBACb,OAAO;aACR,CAAC,CAAC;QACL,CAAC;QAED,IAAI,IAAI,KAAK,cAAc,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,MAAM,CAAC,CAAC;YAC5C,MAAM,IAAI,GAAG,OAAO,CAAC,SAAS,CAAC;YAC/B,IAAI,OAAO,GAAG,EAAE,CAAC;YACjB,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBACnB,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YACpD,CAAC;YACD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,EAAE;gBACxC,UAAU,EAAE,aAAa;gBACzB,IAAI;gBACJ,OAAO;gBACP,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,SAAS;aAClB,CAAC,CAAC;YACH,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,eAAe,CAAC;YAC1C,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QACD,IAAI,IAAI,KAAK,mBAAmB,EAAE,CAAC;YACjC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC,CAAC;YAClD,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;gBACxB,4DAA4D;gBAC5D,4DAA4D;gBAC5D,OAAO,IAAI,CAAC;YACd,CAAC;YACD,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;YACzD,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACvD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,kBAAkB,CAAC,CAAC;YAC/E,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,aAAa,EAAE,MAAM,EAAE,CAAC,CAAC;QAC7D,CAAC;QACD,IAAI,IAAI,KAAK,eAAe,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;YACvD,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC;YAC7C,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;gBACvB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,IAAI,WAAW,CAAC,CAAC;YACtD,CAAC;YACD,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,aAAa,EAAE,OAAO,EAAE,CAAC,CAAC;QACxD,CAAC;QAED,IAAI,IAAI,KAAK,gBAAgB,EAAE,CAAC;YAC9B,IAAI,CAAC,KAAK,CAAC,QAAQ,GAAG,MAAM,CAC1B,UAAU,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAC/D,CAAC;YACF,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,MAAM,CACvB,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,CACtD,CAAC;YACF,OAAO,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;QACzC,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,aAAa,CAAC,OAAgC;QAC5C,IAAI,eAAe,IAAI,OAAO,EAAE,CAAC;YAC/B,IAAI,CAAC,KAAK,CAAC,YAAY,GAAG,KAAK,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QACzD,CAAC;QACD,IAAI,YAAY,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;QACnD,CAAC;IACH,CAAC;IAED,MAAM,CAAC,OAAO,CAAC,KAAkB;QAC/B,MAAM,GAAG,GAAG,MAAM,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC;QAC5D,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;IACxC,CAAC;IAED,MAAM,CAAC,UAAU,CAAC,KAAkB;QAClC,MAAM,OAAO,GAAG,SAAS,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,SAAS,CAAC;QAC/D,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1C,CAAC;IAED,MAAM,CAAC,gBAAgB,CAAC,KAAkB;QACxC,MAAM,GAAG,GAAG,gBAAgB,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,CAAC;QAClE,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAChC,CAAC"}
package/dist/web.d.ts ADDED
@@ -0,0 +1,56 @@
1
+ /** Local web dashboard for observing agent events. */
2
+ import { type EventBus } from "./events.ts";
3
+ export declare const DASHBOARD_HTML = "<!doctype html>\n<html lang=\"zh-CN\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>laoHuangCode Trace</title>\n <style>\n :root { color-scheme: dark; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }\n body { margin: 0; background: #0b1020; color: #dbeafe; }\n header { position: sticky; top: 0; padding: 18px 24px; background: #11182d;\n border-bottom: 1px solid #263450; display: flex; justify-content: space-between; }\n h1 { margin: 0; font: 650 18px/1.4 system-ui, sans-serif; }\n #status { color: #86efac; font: 13px system-ui, sans-serif; }\n main { max-width: 1050px; margin: 0 auto; padding: 20px; }\n .empty { color: #8190ad; text-align: center; padding: 80px 0; }\n .event { display: grid; grid-template-columns: 150px 1fr; gap: 16px; margin: 10px 0;\n padding: 14px 16px; background: #11182d; border: 1px solid #263450; border-radius: 10px; }\n .meta { color: #93c5fd; font-size: 12px; }\n .type { color: #fbbf24; font-weight: 700; margin-bottom: 6px; }\n pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: #d1d5db; }\n @media (max-width: 650px) { .event { grid-template-columns: 1fr; gap: 8px; } }\n </style>\n</head>\n<body>\n <header><h1>laoHuangCode \u00B7 Agent Trace</h1><span id=\"status\">\u8FDE\u63A5\u4E2D</span></header>\n <main id=\"events\"><div class=\"empty\">\u7B49\u5F85 Agent \u4E8B\u4EF6\u2026</div></main>\n <script>\n let lastId = 0;\n let polling = false;\n const events = document.getElementById('events');\n const status = document.getElementById('status');\n function addEvent(event) {\n const empty = events.querySelector('.empty');\n if (empty) empty.remove();\n const card = document.createElement('section');\n card.className = 'event';\n const meta = document.createElement('div');\n meta.className = 'meta';\n meta.textContent = `#${event.id}\n${new Date(event.timestamp).toLocaleTimeString()}`;\n const body = document.createElement('div');\n const type = document.createElement('div');\n type.className = 'type';\n type.textContent = event.type;\n const payload = document.createElement('pre');\n payload.textContent = JSON.stringify(event.payload, null, 2);\n body.append(type, payload);\n card.append(meta, body);\n events.append(card);\n while (events.querySelectorAll('.event').length > 1000) {\n events.querySelector('.event').remove();\n }\n lastId = Math.max(lastId, event.id);\n }\n async function poll() {\n if (polling) return;\n polling = true;\n try {\n const response = await fetch(`/api/events?after=${lastId}`, {cache: 'no-store'});\n if (!response.ok) throw new Error(`HTTP ${response.status}`);\n const data = await response.json();\n data.events.forEach(addEvent);\n status.textContent = '\u5B9E\u65F6\u8FDE\u63A5';\n status.style.color = '#86efac';\n } catch (error) {\n status.textContent = `\u8FDE\u63A5\u5931\u8D25\uFF1A${error.message}`;\n status.style.color = '#fca5a5';\n } finally {\n polling = false;\n }\n }\n poll();\n setInterval(poll, 500);\n </script>\n</body>\n</html>\n";
4
+ /** One entry in the dashboard's in-memory event sequence. */
5
+ export interface LoggedEvent {
6
+ id: number;
7
+ /** UTC ISO-8601 timestamp. */
8
+ timestamp: string;
9
+ type: string;
10
+ payload: Record<string, unknown>;
11
+ }
12
+ /** Envelope-shaped or plain-record input accepted by {@link EventLog.recordEvent}. */
13
+ export interface RecordableEvent {
14
+ kind?: unknown;
15
+ type?: unknown;
16
+ payload?: unknown;
17
+ event_id?: unknown;
18
+ session_id?: unknown;
19
+ task_id?: unknown;
20
+ correlation_id?: unknown;
21
+ sequence?: unknown;
22
+ }
23
+ export interface EventLogOptions {
24
+ maxEvents?: number;
25
+ }
26
+ /** An in-memory sequence of observable agent events. */
27
+ export declare class EventLog {
28
+ private events;
29
+ private readonly maxEvents;
30
+ private nextId;
31
+ constructor(options?: EventLogOptions);
32
+ record(type: string, payload: Record<string, unknown>): LoggedEvent;
33
+ /** Record an EventEnvelope, projected event, or plain event record. */
34
+ recordEvent(event: RecordableEvent): LoggedEvent;
35
+ read(afterId?: number): LoggedEvent[];
36
+ }
37
+ /**
38
+ * Feed the dashboard log from the event bus pull buffer. Resolves once the
39
+ * bus is closed and its remaining buffered events have been drained.
40
+ */
41
+ export declare function consumePullBuffer(bus: EventBus, log: EventLog): Promise<void>;
42
+ export interface WebDashboardOptions {
43
+ host?: string;
44
+ port?: number;
45
+ }
46
+ /** Serve the live event dashboard on the local loopback interface. */
47
+ export declare class WebDashboard {
48
+ private readonly server;
49
+ private readonly host;
50
+ private readonly port;
51
+ private listening;
52
+ constructor(eventLog: EventLog, options?: WebDashboardOptions);
53
+ get url(): string;
54
+ start(): Promise<void>;
55
+ stop(): Promise<void>;
56
+ }
package/dist/web.js ADDED
@@ -0,0 +1,247 @@
1
+ /** Local web dashboard for observing agent events. */
2
+ import { createServer, } from "node:http";
3
+ import { EventBusClosedError, EventProjector, } from "./events.js";
4
+ export const DASHBOARD_HTML = `<!doctype html>
5
+ <html lang="zh-CN">
6
+ <head>
7
+ <meta charset="utf-8">
8
+ <meta name="viewport" content="width=device-width, initial-scale=1">
9
+ <title>laoHuangCode Trace</title>
10
+ <style>
11
+ :root { color-scheme: dark; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
12
+ body { margin: 0; background: #0b1020; color: #dbeafe; }
13
+ header { position: sticky; top: 0; padding: 18px 24px; background: #11182d;
14
+ border-bottom: 1px solid #263450; display: flex; justify-content: space-between; }
15
+ h1 { margin: 0; font: 650 18px/1.4 system-ui, sans-serif; }
16
+ #status { color: #86efac; font: 13px system-ui, sans-serif; }
17
+ main { max-width: 1050px; margin: 0 auto; padding: 20px; }
18
+ .empty { color: #8190ad; text-align: center; padding: 80px 0; }
19
+ .event { display: grid; grid-template-columns: 150px 1fr; gap: 16px; margin: 10px 0;
20
+ padding: 14px 16px; background: #11182d; border: 1px solid #263450; border-radius: 10px; }
21
+ .meta { color: #93c5fd; font-size: 12px; }
22
+ .type { color: #fbbf24; font-weight: 700; margin-bottom: 6px; }
23
+ pre { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; color: #d1d5db; }
24
+ @media (max-width: 650px) { .event { grid-template-columns: 1fr; gap: 8px; } }
25
+ </style>
26
+ </head>
27
+ <body>
28
+ <header><h1>laoHuangCode · Agent Trace</h1><span id="status">连接中</span></header>
29
+ <main id="events"><div class="empty">等待 Agent 事件…</div></main>
30
+ <script>
31
+ let lastId = 0;
32
+ let polling = false;
33
+ const events = document.getElementById('events');
34
+ const status = document.getElementById('status');
35
+ function addEvent(event) {
36
+ const empty = events.querySelector('.empty');
37
+ if (empty) empty.remove();
38
+ const card = document.createElement('section');
39
+ card.className = 'event';
40
+ const meta = document.createElement('div');
41
+ meta.className = 'meta';
42
+ meta.textContent = \`#\${event.id}
43
+ \${new Date(event.timestamp).toLocaleTimeString()}\`;
44
+ const body = document.createElement('div');
45
+ const type = document.createElement('div');
46
+ type.className = 'type';
47
+ type.textContent = event.type;
48
+ const payload = document.createElement('pre');
49
+ payload.textContent = JSON.stringify(event.payload, null, 2);
50
+ body.append(type, payload);
51
+ card.append(meta, body);
52
+ events.append(card);
53
+ while (events.querySelectorAll('.event').length > 1000) {
54
+ events.querySelector('.event').remove();
55
+ }
56
+ lastId = Math.max(lastId, event.id);
57
+ }
58
+ async function poll() {
59
+ if (polling) return;
60
+ polling = true;
61
+ try {
62
+ const response = await fetch(\`/api/events?after=\${lastId}\`, {cache: 'no-store'});
63
+ if (!response.ok) throw new Error(\`HTTP \${response.status}\`);
64
+ const data = await response.json();
65
+ data.events.forEach(addEvent);
66
+ status.textContent = '实时连接';
67
+ status.style.color = '#86efac';
68
+ } catch (error) {
69
+ status.textContent = \`连接失败:\${error.message}\`;
70
+ status.style.color = '#fca5a5';
71
+ } finally {
72
+ polling = false;
73
+ }
74
+ }
75
+ poll();
76
+ setInterval(poll, 500);
77
+ </script>
78
+ </body>
79
+ </html>
80
+ `;
81
+ const METADATA_KEYS = [
82
+ "event_id",
83
+ "session_id",
84
+ "task_id",
85
+ "correlation_id",
86
+ "sequence",
87
+ ];
88
+ /** An in-memory sequence of observable agent events. */
89
+ export class EventLog {
90
+ events = [];
91
+ maxEvents;
92
+ nextId = 1;
93
+ constructor(options = {}) {
94
+ const maxEvents = options.maxEvents ?? 5_000;
95
+ if (maxEvents <= 0) {
96
+ throw new RangeError("maxEvents must be positive");
97
+ }
98
+ this.maxEvents = maxEvents;
99
+ }
100
+ record(type, payload) {
101
+ const event = {
102
+ id: this.nextId,
103
+ timestamp: new Date().toISOString(),
104
+ type,
105
+ payload: { ...payload },
106
+ };
107
+ this.nextId += 1;
108
+ this.events.push(event);
109
+ if (this.events.length > this.maxEvents) {
110
+ this.events.splice(0, this.events.length - this.maxEvents);
111
+ }
112
+ return { ...event };
113
+ }
114
+ /** Record an EventEnvelope, projected event, or plain event record. */
115
+ recordEvent(event) {
116
+ const rawKind = event.kind ?? event.type ?? "event";
117
+ const metadata = {};
118
+ for (const key of METADATA_KEYS) {
119
+ const value = event[key];
120
+ if (value !== null && value !== undefined) {
121
+ metadata[key] = value;
122
+ }
123
+ }
124
+ // Enum-like kinds (with a ``value`` field) flatten to their value.
125
+ const type = rawKind !== null && typeof rawKind === "object" && "value" in rawKind
126
+ ? String(rawKind.value)
127
+ : String(rawKind);
128
+ const payload = event.payload;
129
+ const safePayload = payload !== null && typeof payload === "object" && !Array.isArray(payload)
130
+ ? { ...payload }
131
+ : {};
132
+ return this.record(type, { ...metadata, ...safePayload });
133
+ }
134
+ read(afterId = 0) {
135
+ return this.events
136
+ .filter((event) => event.id > afterId)
137
+ .map((event) => ({ ...event }));
138
+ }
139
+ }
140
+ /**
141
+ * Feed the dashboard log from the event bus pull buffer. Resolves once the
142
+ * bus is closed and its remaining buffered events have been drained.
143
+ */
144
+ export async function consumePullBuffer(bus, log) {
145
+ const projector = new EventProjector();
146
+ for (;;) {
147
+ let event;
148
+ try {
149
+ event = await bus.get();
150
+ }
151
+ catch (error) {
152
+ if (error instanceof EventBusClosedError) {
153
+ return;
154
+ }
155
+ throw error;
156
+ }
157
+ log.recordEvent(projector.project(event, "web"));
158
+ }
159
+ }
160
+ function send(response, status, body, contentType) {
161
+ const data = Buffer.from(body, "utf-8");
162
+ response.writeHead(status, {
163
+ "Content-Type": contentType,
164
+ "Content-Length": data.length,
165
+ "Cache-Control": "no-store",
166
+ });
167
+ response.end(data);
168
+ }
169
+ function sendJson(response, status, value) {
170
+ send(response, status, JSON.stringify(value), "application/json; charset=utf-8");
171
+ }
172
+ /** ``int()``-style parse: optional sign, surrounding whitespace tolerated. */
173
+ function parseAfter(raw) {
174
+ const text = raw === null || raw === "" ? "0" : raw.trim();
175
+ if (!/^[+-]?\d+$/.test(text)) {
176
+ return null;
177
+ }
178
+ return Number.parseInt(text, 10);
179
+ }
180
+ function createRequestHandler(eventLog) {
181
+ return (request, response) => {
182
+ if (request.method !== "GET") {
183
+ send(response, 501, "Unsupported method", "text/plain; charset=utf-8");
184
+ return;
185
+ }
186
+ const url = new URL(request.url ?? "/", "http://localhost");
187
+ if (url.pathname === "/" || url.pathname === "/index.html") {
188
+ send(response, 200, DASHBOARD_HTML, "text/html; charset=utf-8");
189
+ return;
190
+ }
191
+ if (url.pathname === "/api/events") {
192
+ const afterId = parseAfter(url.searchParams.get("after"));
193
+ if (afterId === null) {
194
+ sendJson(response, 400, { error: "after must be an integer" });
195
+ return;
196
+ }
197
+ sendJson(response, 200, { events: eventLog.read(afterId) });
198
+ return;
199
+ }
200
+ sendJson(response, 404, { error: "not found" });
201
+ };
202
+ }
203
+ /** Serve the live event dashboard on the local loopback interface. */
204
+ export class WebDashboard {
205
+ server;
206
+ host;
207
+ port;
208
+ listening = false;
209
+ constructor(eventLog, options = {}) {
210
+ this.host = options.host ?? "127.0.0.1";
211
+ this.port = options.port ?? 8765;
212
+ this.server = createServer(createRequestHandler(eventLog));
213
+ }
214
+ get url() {
215
+ const address = this.server.address();
216
+ if (address !== null && typeof address === "object") {
217
+ return `http://${address.address}:${address.port}/`;
218
+ }
219
+ return `http://${this.host}:${this.port}/`;
220
+ }
221
+ start() {
222
+ if (this.listening) {
223
+ return Promise.resolve();
224
+ }
225
+ return new Promise((resolve, reject) => {
226
+ this.server.once("error", reject);
227
+ this.server.listen(this.port, this.host, () => {
228
+ this.listening = true;
229
+ resolve();
230
+ });
231
+ });
232
+ }
233
+ stop() {
234
+ if (!this.listening) {
235
+ return Promise.resolve();
236
+ }
237
+ this.listening = false;
238
+ return new Promise((resolve) => {
239
+ this.server.close(() => {
240
+ resolve();
241
+ });
242
+ // Keep-alive clients (e.g. undici fetch) would otherwise delay close.
243
+ this.server.closeIdleConnections();
244
+ });
245
+ }
246
+ }
247
+ //# sourceMappingURL=web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"web.js","sourceRoot":"","sources":["../src/web.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,36 +1,39 @@
1
1
  {
2
2
  "name": "laohuang",
3
- "version": "0.3.2",
4
- "description": "Node.js launcher for the laoHuangCode coding agent",
5
- "license": "MIT",
3
+ "version": "0.4.0",
4
+ "description": "A minimal coding agent CLI built on the official openai SDK and Chat Completions native tool calling.",
5
+ "type": "module",
6
6
  "repository": {
7
7
  "type": "git",
8
- "url": "https://github.com/hxr223/laoHuangCode",
9
- "directory": "npm"
8
+ "url": "https://github.com/hxr223/laoHuangCode"
10
9
  },
11
10
  "homepage": "https://github.com/hxr223/laoHuangCode#readme",
12
11
  "bugs": "https://github.com/hxr223/laoHuangCode/issues",
13
- "type": "commonjs",
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
14
15
  "bin": {
15
- "laohuang": "bin/laohuang.js"
16
+ "laohuang": "dist/cli.js"
16
17
  },
17
- "files": [
18
- "bin",
19
- "lib",
20
- "vendor",
21
- "README.md"
22
- ],
23
18
  "engines": {
24
19
  "node": ">=18"
25
20
  },
26
- "os": [
27
- "darwin",
28
- "linux"
21
+ "scripts": {
22
+ "build": "tsc",
23
+ "test": "node --test \"test/**/*.test.ts\"",
24
+ "prepack": "npm run build"
25
+ },
26
+ "files": [
27
+ "dist",
28
+ "README.md",
29
+ "LICENSE"
29
30
  ],
30
- "publishConfig": {
31
- "access": "public"
31
+ "dependencies": {
32
+ "openai": "^4.0.0"
32
33
  },
33
- "scripts": {
34
- "test": "node --test"
35
- }
34
+ "devDependencies": {
35
+ "@types/node": "^22.0.0",
36
+ "typescript": "^5.5.0"
37
+ },
38
+ "license": "MIT"
36
39
  }
package/bin/laohuang.js DELETED
@@ -1,10 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- const { run } = require("../lib/launcher");
4
-
5
- try {
6
- process.exitCode = run(process.argv.slice(2));
7
- } catch (error) {
8
- console.error(`laohuang: ${error.message}`);
9
- process.exitCode = 1;
10
- }