@hayasaka7/haya-pet 0.1.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.
- package/.gitattributes +34 -0
- package/.github/workflows/release.yml +61 -0
- package/LICENSE +21 -0
- package/README.md +247 -0
- package/apps/cli/src/haya-pet.js +395 -0
- package/apps/cli/test/haya-pet.test.mjs +339 -0
- package/apps/companion/README.md +83 -0
- package/apps/companion/package.json +17 -0
- package/apps/companion/src/main/display-manager.js +71 -0
- package/apps/companion/src/main/index.js +349 -0
- package/apps/companion/src/main/lock-file.js +52 -0
- package/apps/companion/src/main/panel-placement.js +45 -0
- package/apps/companion/src/main/pet-loader.js +2 -0
- package/apps/companion/src/main/position-store.js +3 -0
- package/apps/companion/src/main/preload.cjs +13 -0
- package/apps/companion/src/main/state-file.js +2 -0
- package/apps/companion/src/main/terminal-helper-client.js +79 -0
- package/apps/companion/src/main/terminal-locator.js +44 -0
- package/apps/companion/src/main/tray-menu.js +79 -0
- package/apps/companion/src/main/window-options.js +66 -0
- package/apps/companion/src/renderer/index.html +18 -0
- package/apps/companion/src/renderer/interaction-controller.js +114 -0
- package/apps/companion/src/renderer/pet-window.js +275 -0
- package/apps/companion/src/renderer/session-bubbles.js +138 -0
- package/apps/companion/src/renderer/styles.css +225 -0
- package/apps/companion/src/renderer/task-talk-window.js +141 -0
- package/apps/companion/test/display-manager.test.mjs +48 -0
- package/apps/companion/test/interaction-controller.test.mjs +107 -0
- package/apps/companion/test/panel-placement.test.mjs +60 -0
- package/apps/companion/test/position-store.test.mjs +54 -0
- package/apps/companion/test/state-file.test.mjs +52 -0
- package/apps/companion/test/terminal-helper-client.test.mjs +68 -0
- package/apps/companion/test/terminal-locator.test.mjs +35 -0
- package/apps/companion/test/tray-menu.test.mjs +45 -0
- package/apps/companion/test/window-options.test.mjs +62 -0
- package/apps/pet-preview/index.html +42 -0
- package/apps/pet-preview/src/preview-app.js +123 -0
- package/apps/pet-preview/src/preview-state.js +70 -0
- package/apps/pet-preview/src/preview.css +125 -0
- package/apps/pet-preview/test/preview-state.test.mjs +62 -0
- package/assets/fallback-pet/README.md +16 -0
- package/assets/fallback-pet/pet.json +13 -0
- package/docs/architecture.md +144 -0
- package/docs/known-issues.md +49 -0
- package/docs/publishing.md +48 -0
- package/docs/screenshots/README.md +7 -0
- package/docs/screenshots/folder-collapsed.png +0 -0
- package/docs/screenshots/hero.png +0 -0
- package/docs/screenshots/pet-overlay.png +0 -0
- package/docs/screenshots/session-bubbles.png +0 -0
- package/docs/screenshots/tray-menu.png +0 -0
- package/docs/troubleshooting.md +36 -0
- package/native/README.md +80 -0
- package/native/linux-window-helper/README.md +29 -0
- package/native/mac-window-helper/README.md +30 -0
- package/native/win-window-helper/Program.cs +312 -0
- package/native/win-window-helper/README.md +53 -0
- package/native/win-window-helper/win-window-helper.csproj +12 -0
- package/package.json +35 -0
- package/packages/adapters/src/adapter-info.js +61 -0
- package/packages/adapters/src/capabilities.js +39 -0
- package/packages/adapters/src/heuristics.js +114 -0
- package/packages/adapters/src/output-observer.js +164 -0
- package/packages/adapters/src/routing.js +86 -0
- package/packages/adapters/test/adapter-info.test.mjs +35 -0
- package/packages/adapters/test/capabilities.test.mjs +44 -0
- package/packages/adapters/test/heuristics.test.mjs +42 -0
- package/packages/adapters/test/output-observer.test.mjs +142 -0
- package/packages/adapters/test/routing.test.mjs +93 -0
- package/packages/app-state/src/state-file.js +53 -0
- package/packages/app-state/src/state.js +80 -0
- package/packages/app-state/test/state.test.mjs +36 -0
- package/packages/cli-core/src/companion-launcher.js +69 -0
- package/packages/cli-core/src/pty-runner.js +96 -0
- package/packages/cli-core/src/run-command.js +353 -0
- package/packages/cli-core/src/strip-ansi.js +16 -0
- package/packages/cli-core/test/companion-launcher.test.mjs +98 -0
- package/packages/cli-core/test/run-command.test.mjs +177 -0
- package/packages/cli-core/test/strip-ansi.test.mjs +27 -0
- package/packages/daemon-core/src/daemon-runtime.js +49 -0
- package/packages/daemon-core/src/ipc-server.js +180 -0
- package/packages/daemon-core/src/ipc-transport.js +70 -0
- package/packages/daemon-core/src/singleton.js +46 -0
- package/packages/daemon-core/test/daemon-runtime.test.mjs +65 -0
- package/packages/daemon-core/test/ipc-server.test.mjs +70 -0
- package/packages/daemon-core/test/ipc-transport.test.mjs +72 -0
- package/packages/daemon-core/test/singleton.test.mjs +32 -0
- package/packages/pet-core/src/animation-state.js +84 -0
- package/packages/pet-core/src/animator.js +26 -0
- package/packages/pet-core/src/atlas.js +81 -0
- package/packages/pet-core/src/discovery.js +90 -0
- package/packages/pet-core/src/manifest.js +112 -0
- package/packages/pet-core/src/validation.js +43 -0
- package/packages/pet-core/test/animation-state.test.mjs +47 -0
- package/packages/pet-core/test/animator.test.mjs +31 -0
- package/packages/pet-core/test/atlas.test.mjs +81 -0
- package/packages/pet-core/test/discovery.test.mjs +93 -0
- package/packages/pet-core/test/manifest.test.mjs +93 -0
- package/packages/pet-core/test/validation.test.mjs +69 -0
- package/packages/platform-core/src/capabilities.js +49 -0
- package/packages/platform-core/src/paths.js +75 -0
- package/packages/platform-core/src/platform.js +15 -0
- package/packages/platform-core/test/platform.test.mjs +84 -0
- package/packages/protocol/src/messages.js +156 -0
- package/packages/protocol/test/messages.test.mjs +112 -0
- package/packages/session-core/src/bubble-linger.js +47 -0
- package/packages/session-core/src/bubble-view.js +79 -0
- package/packages/session-core/src/pet-state.js +56 -0
- package/packages/session-core/src/priority.js +56 -0
- package/packages/session-core/src/registry.js +144 -0
- package/packages/session-core/src/summaries.js +54 -0
- package/packages/session-core/test/bubble-linger.test.mjs +96 -0
- package/packages/session-core/test/bubble-view.test.mjs +79 -0
- package/packages/session-core/test/pet-state.test.mjs +118 -0
- package/packages/session-core/test/priority.test.mjs +53 -0
- package/packages/session-core/test/registry.test.mjs +161 -0
- package/packages/session-core/test/summaries.test.mjs +38 -0
- package/packages/task-core/src/approvals.js +91 -0
- package/packages/task-core/src/controls.js +61 -0
- package/packages/task-core/src/replies.js +80 -0
- package/packages/task-core/src/task-events.js +101 -0
- package/packages/task-core/src/task-status.js +93 -0
- package/packages/task-core/src/task-store.js +74 -0
- package/packages/task-core/test/approvals.test.mjs +61 -0
- package/packages/task-core/test/controls.test.mjs +61 -0
- package/packages/task-core/test/replies.test.mjs +51 -0
- package/packages/task-core/test/task-events.test.mjs +67 -0
- package/packages/task-core/test/task-status.test.mjs +49 -0
- package/packages/task-core/test/task-store.test.mjs +65 -0
- package/test/harness.mjs +22 -0
- package/test/run-tests.mjs +47 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "../../../test/harness.mjs";
|
|
3
|
+
import {
|
|
4
|
+
TASK_STATUSES,
|
|
5
|
+
isTaskStatus,
|
|
6
|
+
mapTaskStatusToAiState,
|
|
7
|
+
mapTaskStatusToPetAction,
|
|
8
|
+
buildTaskStatusPill
|
|
9
|
+
} from "../src/task-status.js";
|
|
10
|
+
|
|
11
|
+
test("defines the documented task status vocabulary", () => {
|
|
12
|
+
for (const status of [
|
|
13
|
+
"starting",
|
|
14
|
+
"running_command",
|
|
15
|
+
"editing_files",
|
|
16
|
+
"waiting_approval",
|
|
17
|
+
"completed",
|
|
18
|
+
"paused",
|
|
19
|
+
"stopped"
|
|
20
|
+
]) {
|
|
21
|
+
assert.ok(TASK_STATUSES.includes(status), `missing ${status}`);
|
|
22
|
+
}
|
|
23
|
+
assert.equal(isTaskStatus("running_command"), true);
|
|
24
|
+
assert.equal(isTaskStatus("nope"), false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("maps detailed task status down to normalized AI state", () => {
|
|
28
|
+
assert.equal(mapTaskStatusToAiState("running_command"), "running_tool");
|
|
29
|
+
assert.equal(mapTaskStatusToAiState("waiting_approval"), "waiting_approval");
|
|
30
|
+
assert.equal(mapTaskStatusToAiState("reviewing"), "reviewing");
|
|
31
|
+
assert.equal(mapTaskStatusToAiState("completed"), "success");
|
|
32
|
+
assert.equal(mapTaskStatusToAiState("paused"), "idle");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("maps task status to pet action through the AI state", () => {
|
|
36
|
+
assert.equal(mapTaskStatusToPetAction("running_command"), "running");
|
|
37
|
+
assert.equal(mapTaskStatusToPetAction("waiting_approval"), "waiting");
|
|
38
|
+
assert.equal(mapTaskStatusToPetAction("completed"), "jumping");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test("renders stable status pills", () => {
|
|
42
|
+
assert.equal(buildTaskStatusPill("running_command"), "Running command");
|
|
43
|
+
assert.equal(buildTaskStatusPill("waiting_user"), "Waiting for you");
|
|
44
|
+
assert.equal(buildTaskStatusPill("compacting"), "Compacting context");
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test("throws on unknown task status mapping", () => {
|
|
48
|
+
assert.throws(() => mapTaskStatusToAiState("bogus"), /Unknown task status/);
|
|
49
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { test } from "../../../test/harness.mjs";
|
|
3
|
+
import { createTaskStore } from "../src/task-store.js";
|
|
4
|
+
|
|
5
|
+
test("appends events and returns recent history per session", () => {
|
|
6
|
+
const store = createTaskStore();
|
|
7
|
+
|
|
8
|
+
store.appendEvent({ id: "e1", sessionId: "s", type: "user_message", timestamp: 1, text: "hi" });
|
|
9
|
+
store.appendEvent({ id: "e2", sessionId: "s", type: "assistant_message", timestamp: 2, text: "yo" });
|
|
10
|
+
|
|
11
|
+
const events = store.getEvents("s");
|
|
12
|
+
assert.equal(events.length, 2);
|
|
13
|
+
assert.equal(store.getLatestEvent("s").id, "e2");
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test("rejects invalid events", () => {
|
|
17
|
+
const store = createTaskStore();
|
|
18
|
+
assert.throws(() => store.appendEvent({ type: "user_message" }), /sessionId/);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test("derives a status snapshot from status-bearing events", () => {
|
|
22
|
+
const store = createTaskStore();
|
|
23
|
+
store.appendEvent({
|
|
24
|
+
id: "e1",
|
|
25
|
+
sessionId: "s",
|
|
26
|
+
type: "status_changed",
|
|
27
|
+
timestamp: 10,
|
|
28
|
+
status: "running_command",
|
|
29
|
+
confidence: 0.8,
|
|
30
|
+
source: "pty_output",
|
|
31
|
+
text: "running tests"
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const snapshot = store.getStatusSnapshot("s");
|
|
35
|
+
assert.equal(snapshot.sessionId, "s");
|
|
36
|
+
assert.equal(snapshot.status, "running_command");
|
|
37
|
+
assert.equal(snapshot.aiState, "running_tool");
|
|
38
|
+
assert.equal(snapshot.petAction, "running");
|
|
39
|
+
assert.equal(snapshot.confidence, 0.8);
|
|
40
|
+
assert.equal(snapshot.source, "pty_output");
|
|
41
|
+
assert.equal(snapshot.updatedAt, 10);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
test("caps stored history to the configured maximum", () => {
|
|
45
|
+
const store = createTaskStore({ maxEventsPerSession: 2 });
|
|
46
|
+
for (let i = 0; i < 5; i += 1) {
|
|
47
|
+
store.appendEvent({ id: `e${i}`, sessionId: "s", type: "tool_output", timestamp: i });
|
|
48
|
+
}
|
|
49
|
+
const events = store.getEvents("s");
|
|
50
|
+
assert.equal(events.length, 2);
|
|
51
|
+
assert.deepEqual(events.map((e) => e.id), ["e3", "e4"]);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("limits returned events when a limit is requested", () => {
|
|
55
|
+
const store = createTaskStore();
|
|
56
|
+
for (let i = 0; i < 5; i += 1) {
|
|
57
|
+
store.appendEvent({ id: `e${i}`, sessionId: "s", type: "tool_output", timestamp: i });
|
|
58
|
+
}
|
|
59
|
+
assert.equal(store.getEvents("s", { limit: 2 }).length, 2);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("returns an undefined snapshot for unknown sessions", () => {
|
|
63
|
+
const store = createTaskStore();
|
|
64
|
+
assert.equal(store.getStatusSnapshot("missing"), undefined);
|
|
65
|
+
});
|
package/test/harness.mjs
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const tests = [];
|
|
2
|
+
|
|
3
|
+
export function test(name, fn) {
|
|
4
|
+
tests.push({ name, fn });
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export async function runRegisteredTests() {
|
|
8
|
+
let failed = 0;
|
|
9
|
+
|
|
10
|
+
for (const entry of tests) {
|
|
11
|
+
try {
|
|
12
|
+
await entry.fn();
|
|
13
|
+
console.log(`ok - ${entry.name}`);
|
|
14
|
+
} catch (error) {
|
|
15
|
+
failed += 1;
|
|
16
|
+
console.error(`not ok - ${entry.name}`);
|
|
17
|
+
console.error(error && error.stack ? error.stack : error);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return { total: tests.length, failed };
|
|
22
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { readdir } from "node:fs/promises";
|
|
2
|
+
import { join, relative, sep } from "node:path";
|
|
3
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
4
|
+
import { runRegisteredTests } from "./harness.mjs";
|
|
5
|
+
|
|
6
|
+
async function findTestFiles(dir) {
|
|
7
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
8
|
+
const files = [];
|
|
9
|
+
|
|
10
|
+
for (const entry of entries) {
|
|
11
|
+
const absolute = join(dir, entry.name);
|
|
12
|
+
if (entry.isDirectory()) {
|
|
13
|
+
files.push(...await findTestFiles(absolute));
|
|
14
|
+
} else if (entry.name.endsWith(".test.mjs")) {
|
|
15
|
+
files.push(absolute);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const root = fileURLToPath(new URL("..", import.meta.url));
|
|
23
|
+
const testRoots = ["packages", "apps"];
|
|
24
|
+
const testFiles = [];
|
|
25
|
+
|
|
26
|
+
for (const testRoot of testRoots) {
|
|
27
|
+
try {
|
|
28
|
+
testFiles.push(...await findTestFiles(join(root, testRoot)));
|
|
29
|
+
} catch (error) {
|
|
30
|
+
if (error.code !== "ENOENT") {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
for (const file of testFiles) {
|
|
37
|
+
const displayPath = relative(root, file).split(sep).join("/");
|
|
38
|
+
console.log(`# ${displayPath}`);
|
|
39
|
+
await import(pathToFileURL(file));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const result = await runRegisteredTests();
|
|
43
|
+
console.log(`# ${result.total - result.failed}/${result.total} tests passed`);
|
|
44
|
+
|
|
45
|
+
if (result.failed > 0) {
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
}
|