@tea-agent/loop-agent 0.34.2 → 0.34.4

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 (29) hide show
  1. package/AGENTS.md +7 -2
  2. package/CHANGELOG.md +65 -22
  3. package/dist/application/task-lifecycle/advance.js +5 -1
  4. package/dist/task/source-prepare/index.js +1 -0
  5. package/dist/task/source-prepare/placeholder-paths.js +77 -0
  6. package/dist/task/source-prepare/semantic-intake.js +192 -27
  7. package/dist/worker/console/app-data.js +2 -0
  8. package/dist/worker/console/chat/chat-event-store.js +85 -3
  9. package/dist/worker/console/chat/pi-runtime.js +230 -62
  10. package/dist/worker/console/chat/resource-preferences-store.js +152 -0
  11. package/dist/worker/console/chat/routes.js +401 -18
  12. package/dist/worker/console/chat/turn-execution-registry.js +82 -0
  13. package/dist/worker/console/dag-execution-receipt.js +14 -1
  14. package/dist/worker/console/prd-intake-bridge.js +51 -10
  15. package/dist/worker/console/server.js +4 -0
  16. package/dist/worker/console/static/assets/index-B6Qdbk8V.js +29 -0
  17. package/dist/worker/console/static/assets/index-Bt0NUxcQ.css +1 -0
  18. package/dist/worker/console/static/index.html +2 -2
  19. package/dist/worker/console/static-src/operator-chat/refs.js +24 -0
  20. package/dist/worker/console/static-src/operator-chat/resource-auto-invocation.js +91 -0
  21. package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +690 -0
  22. package/dist/worker/console/static-src/operator-chat/turn-submission.js +158 -0
  23. package/dist/worker/console/static-src/operator-chat/useChatStream.js +535 -86
  24. package/dist/workflows/dag/init-hybrid.js +2 -0
  25. package/docs/operations/local-development-environment.md +4 -2
  26. package/docs/templates/branch-merge-report.md +9 -0
  27. package/package.json +3 -2
  28. package/dist/worker/console/static/assets/index-BQkhJpV8.css +0 -1
  29. package/dist/worker/console/static/assets/index-BpuHmlSP.js +0 -29
@@ -0,0 +1,152 @@
1
+ /**
2
+ * Operator Chat — repo-scoped resource auto-invocation preferences.
3
+ *
4
+ * Persists under Console app-data `resource-preferences.json` (per repo
5
+ * fingerprint). Preferences never touch SKILL.md, Pi settings, or tracked repo
6
+ * files. Malformed on-disk data fails closed.
7
+ */
8
+ import { createHash } from "node:crypto";
9
+ import { existsSync, lstatSync } from "node:fs";
10
+ import { readJsonIfExists, writeSecureJson, } from "../app-data.js";
11
+ export const RESOURCE_PREFERENCES_SCHEMA_VERSION = 1;
12
+ export class ResourcePreferencesError extends Error {
13
+ code;
14
+ constructor(code, message) {
15
+ super(message);
16
+ this.name = "ResourcePreferencesError";
17
+ this.code = code;
18
+ }
19
+ }
20
+ /** Stable server-issued resource id for a discovered skill (path + name). */
21
+ export function skillResourceId(skill) {
22
+ return createHash("sha256")
23
+ .update(`skill\0${skill.name}\0${skill.filePath}`, "utf8")
24
+ .digest("hex")
25
+ .slice(0, 32);
26
+ }
27
+ function emptyPreferences() {
28
+ return {
29
+ schemaVersion: RESOURCE_PREFERENCES_SCHEMA_VERSION,
30
+ skills: {},
31
+ };
32
+ }
33
+ function isPlainObject(value) {
34
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
35
+ }
36
+ /**
37
+ * Parse and validate on-disk preferences. Malformed content fails closed with
38
+ * a structured error (never silently treated as empty-success).
39
+ */
40
+ export function parseResourcePreferences(raw) {
41
+ if (raw === undefined)
42
+ return emptyPreferences();
43
+ if (!isPlainObject(raw)) {
44
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", "resource-preferences.json must be a JSON object");
45
+ }
46
+ if (raw.schemaVersion !== RESOURCE_PREFERENCES_SCHEMA_VERSION) {
47
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", `unsupported resource-preferences schemaVersion: ${String(raw.schemaVersion)}`);
48
+ }
49
+ if (!isPlainObject(raw.skills)) {
50
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", "resource-preferences.skills must be an object");
51
+ }
52
+ const skills = {};
53
+ for (const [resourceId, entry] of Object.entries(raw.skills)) {
54
+ if (typeof resourceId !== "string" || !resourceId.trim()) {
55
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", "resource-preferences skill key must be a non-empty string");
56
+ }
57
+ if (!isPlainObject(entry)) {
58
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", `resource-preferences entry for ${resourceId} must be an object`);
59
+ }
60
+ if (typeof entry.autoInvocationEnabled !== "boolean") {
61
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", `resource-preferences entry for ${resourceId} missing boolean autoInvocationEnabled`);
62
+ }
63
+ if (typeof entry.updatedAt !== "string" || !entry.updatedAt.trim()) {
64
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", `resource-preferences entry for ${resourceId} missing updatedAt`);
65
+ }
66
+ skills[resourceId] = {
67
+ autoInvocationEnabled: entry.autoInvocationEnabled,
68
+ updatedAt: entry.updatedAt,
69
+ };
70
+ }
71
+ return {
72
+ schemaVersion: RESOURCE_PREFERENCES_SCHEMA_VERSION,
73
+ skills,
74
+ };
75
+ }
76
+ export async function loadResourcePreferences(filePath) {
77
+ if (!filePath)
78
+ return emptyPreferences();
79
+ if (existsSync(filePath)) {
80
+ const st = lstatSync(filePath);
81
+ if (st.isSymbolicLink() || !st.isFile()) {
82
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", `refusing to read non-regular resource-preferences file: ${filePath}`);
83
+ }
84
+ }
85
+ let raw;
86
+ try {
87
+ raw = await readJsonIfExists(filePath);
88
+ }
89
+ catch (error) {
90
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_MALFORMED", error instanceof Error ? error.message : String(error));
91
+ }
92
+ return parseResourcePreferences(raw);
93
+ }
94
+ /** resourceIds whose auto-invocation is explicitly disabled (default = enabled). */
95
+ export function disabledAutoInvocationIds(prefs) {
96
+ const disabled = new Set();
97
+ for (const [id, entry] of Object.entries(prefs.skills)) {
98
+ if (entry.autoInvocationEnabled === false)
99
+ disabled.add(id);
100
+ }
101
+ return disabled;
102
+ }
103
+ export function isAutoInvocationEnabled(prefs, resourceId) {
104
+ const entry = prefs.skills[resourceId];
105
+ if (!entry)
106
+ return true;
107
+ return entry.autoInvocationEnabled !== false;
108
+ }
109
+ export async function setSkillAutoInvocationPreference(options) {
110
+ const current = await loadResourcePreferences(options.filePath);
111
+ const next = {
112
+ schemaVersion: RESOURCE_PREFERENCES_SCHEMA_VERSION,
113
+ skills: {
114
+ ...current.skills,
115
+ [options.resourceId]: {
116
+ autoInvocationEnabled: options.autoInvocationEnabled,
117
+ updatedAt: options.now ?? new Date().toISOString(),
118
+ },
119
+ },
120
+ };
121
+ // Default-on entries may be pruned to keep the file sparse when re-enabled.
122
+ if (options.autoInvocationEnabled === true) {
123
+ // Keep explicit true only when previously present was false; either way
124
+ // store the affirmative so UI/history can show last toggle time.
125
+ }
126
+ try {
127
+ await writeSecureJson(options.filePath, next);
128
+ }
129
+ catch (error) {
130
+ throw new ResourcePreferencesError("PI_RESOURCE_PREFERENCES_WRITE_FAILED", error instanceof Error ? error.message : String(error));
131
+ }
132
+ return next;
133
+ }
134
+ /**
135
+ * Apply stored auto-invocation preferences onto a discovered skill list.
136
+ * Skills remain in inventory (manual /skill:name stays available); only
137
+ * `disableModelInvocation` is forced true when the preference is off.
138
+ * Existing frontmatter disableModelInvocation is never cleared.
139
+ */
140
+ export function applySkillAutoInvocationPreferences(skills, prefs) {
141
+ const disabled = disabledAutoInvocationIds(prefs);
142
+ if (disabled.size === 0)
143
+ return skills;
144
+ return skills.map((skill) => {
145
+ const id = skillResourceId(skill);
146
+ if (!disabled.has(id))
147
+ return skill;
148
+ if (skill.disableModelInvocation === true)
149
+ return skill;
150
+ return { ...skill, disableModelInvocation: true };
151
+ });
152
+ }