gestalt-mobile 0.17.3 → 0.18.1

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 (45) hide show
  1. package/README.md +5 -1
  2. package/dist/client/assets/index-CJBtrNWw.css +1 -0
  3. package/dist/client/assets/index-D4ZAKUN3.js +27 -0
  4. package/dist/client/index.html +2 -2
  5. package/dist/server/server/app.js +6 -0
  6. package/dist/server/server/composition.js +383 -26
  7. package/dist/server/server/features/agent-activity/model.js +197 -0
  8. package/dist/server/server/features/agent-activity/registry.js +155 -0
  9. package/dist/server/server/features/autopilot/application/policy.js +55 -0
  10. package/dist/server/server/features/autopilot/application/ports.js +6 -0
  11. package/dist/server/server/features/autopilot/application/service.js +542 -0
  12. package/dist/server/server/features/autopilot/domain/autopilot-session.js +40 -0
  13. package/dist/server/server/features/autopilot/register-routes.js +9 -0
  14. package/dist/server/server/features/autopilot/toggle/endpoint.js +32 -0
  15. package/dist/server/server/features/org-plan-attention/application/ports.js +6 -0
  16. package/dist/server/server/features/org-plan-attention/get-active/endpoint.js +22 -0
  17. package/dist/server/server/features/org-plan-attention/register-routes.js +11 -0
  18. package/dist/server/server/features/org-plan-attention/resolve/endpoint.js +42 -0
  19. package/dist/server/server/features/plans/application/parse-supervised-plan.js +4 -0
  20. package/dist/server/server/features/sessions/get-history/endpoint.js +62 -2
  21. package/dist/server/server/features/sessions/get-history/history-mapper.js +17 -5
  22. package/dist/server/server/features/sessions/get-session/endpoint.js +3 -1
  23. package/dist/server/server/features/sessions/interaction/kind.js +1 -0
  24. package/dist/server/server/features/sessions/interaction/response-validator.js +5 -1
  25. package/dist/server/server/features/sessions/list-sessions/endpoint.js +2 -0
  26. package/dist/server/server/features/sessions/model/relay-session.js +3 -0
  27. package/dist/server/server/features/sessions/refresh-activity/endpoint.js +14 -0
  28. package/dist/server/server/features/sessions/register-routes.js +15 -2
  29. package/dist/server/server/features/sessions/restore-session/endpoint.js +21 -5
  30. package/dist/server/server/features/skills/list-available/endpoint.js +3 -4
  31. package/dist/server/server/features/skills/model/skill-profile.js +13 -5
  32. package/dist/server/server/platform/codex/activity-facts.js +88 -0
  33. package/dist/server/server/platform/codex/server-request.js +12 -4
  34. package/dist/server/server/platform/codex/session-runtime.js +91 -8
  35. package/dist/server/server/platform/persistence/migrate.js +16 -1
  36. package/dist/server/server/platform/persistence/sqlite-autopilot-store.js +151 -0
  37. package/dist/server/server/platform/persistence/sqlite-event-journal.js +92 -4
  38. package/dist/server/server/platform/persistence/sqlite-pending-interaction-store.js +57 -8
  39. package/dist/server/server/platform/persistence/sqlite-session-repository.js +5 -2
  40. package/dist/server/server/platform/plans/filesystem-workspace-plan-catalog.js +43 -12
  41. package/dist/server/shared/contracts/org-plan-attention.js +101 -0
  42. package/package.json +1 -1
  43. package/dist/client/assets/index-Ba_4zoY1.js +0 -25
  44. package/dist/client/assets/index-e2lJ5XqL.css +0 -1
  45. package/dist/server/server/platform/plans/org-plan-command-validator.js +0 -62
@@ -9,14 +9,12 @@ import { parseSupervisedPlan } from '../../features/plans/application/parse-supe
9
9
  const maximumBytes = 1_048_576;
10
10
  /**
11
11
  * Passive access to every regular `.org` file below a workspace. Symlinks are
12
- * never followed, and every candidate must pass the installed Org Plan helper
13
- * before it is projected for the browser.
12
+ * never followed. Supported supervised plans include a browser preview; other
13
+ * Org files remain visible in the catalog without one.
14
14
  */
15
15
  export class FilesystemWorkspacePlanCatalog {
16
- validator;
17
16
  filesystem;
18
- constructor(validator, filesystem = { lstat, readdir, readFile, realpath, stat }) {
19
- this.validator = validator;
17
+ constructor(filesystem = { lstat, readdir, readFile, realpath, stat }) {
20
18
  this.filesystem = filesystem;
21
19
  }
22
20
  async list(workspacePath) {
@@ -26,10 +24,21 @@ export class FilesystemWorkspacePlanCatalog {
26
24
  const planNames = await this.discover(workspace);
27
25
  const entries = [];
28
26
  for (const planName of planNames) {
29
- const result = await this.readFromWorkspace(workspace, planName);
30
- if (result.kind !== 'available')
27
+ const candidate = await this.readCandidate(workspace, planName);
28
+ if (candidate.kind === 'missing')
31
29
  continue;
32
- entries.push(toEntry(planName, result.plan));
30
+ if (candidate.kind === 'unavailable') {
31
+ entries.push(toFallbackEntry(planName));
32
+ continue;
33
+ }
34
+ const parsed = parseSupervisedPlan({
35
+ source: candidate.source,
36
+ planPath: candidate.canonicalPath,
37
+ workspacePath: workspace,
38
+ });
39
+ entries.push(parsed.kind === 'available'
40
+ ? toEntry(planName, parsed.plan)
41
+ : toFallbackEntry(planName, candidate.source));
33
42
  }
34
43
  return entries;
35
44
  }
@@ -94,6 +103,17 @@ export class FilesystemWorkspacePlanCatalog {
94
103
  return plans.sort((left, right) => left.localeCompare(right));
95
104
  }
96
105
  async readFromWorkspace(workspace, planName) {
106
+ const candidate = await this.readCandidate(workspace, planName);
107
+ if (candidate.kind !== 'readable')
108
+ return candidate;
109
+ const parsed = parseSupervisedPlan({
110
+ source: candidate.source,
111
+ planPath: candidate.canonicalPath,
112
+ workspacePath: workspace,
113
+ });
114
+ return parsed.kind === 'available' ? parsed : { kind: 'unavailable' };
115
+ }
116
+ async readCandidate(workspace, planName) {
97
117
  if (!isPlanPath(planName))
98
118
  return { kind: 'missing' };
99
119
  const path = resolve(workspace, ...planName.split('/'));
@@ -108,8 +128,6 @@ export class FilesystemWorkspacePlanCatalog {
108
128
  toPlanName(relative(workspace, canonical)) !== planName)
109
129
  return { kind: 'unavailable' };
110
130
  const source = await this.filesystem.readFile(canonical, 'utf8');
111
- if (!(await this.validator.validate(workspace, canonical)))
112
- return { kind: 'unavailable' };
113
131
  const after = await this.filesystem.stat(canonical);
114
132
  if (!after.isFile() ||
115
133
  after.size !== before.size ||
@@ -117,8 +135,7 @@ export class FilesystemWorkspacePlanCatalog {
117
135
  after.dev !== before.dev ||
118
136
  after.mtimeMs !== before.mtimeMs)
119
137
  return { kind: 'unavailable' };
120
- const parsed = parseSupervisedPlan({ source, planPath: canonical, workspacePath: workspace });
121
- return parsed.kind === 'available' ? parsed : { kind: 'unavailable' };
138
+ return { kind: 'readable', source, canonicalPath: canonical };
122
139
  }
123
140
  catch (error) {
124
141
  return error.code === 'ENOENT'
@@ -153,8 +170,22 @@ function toEntry(planName, plan) {
153
170
  ...(plan.subtitle === undefined ? {} : { subtitle: plan.subtitle }),
154
171
  ...(plan.date === undefined ? {} : { date: plan.date }),
155
172
  ...(plan.keywords === undefined ? {} : { keywords: plan.keywords }),
173
+ previewAvailable: true,
156
174
  totalSteps: plan.totalSteps,
157
175
  doneSteps: plan.doneSteps,
158
176
  allDone: plan.allDone,
159
177
  };
160
178
  }
179
+ function toFallbackEntry(planName, source) {
180
+ const declaredTitle = source
181
+ ?.replace(/\r\n?/g, '\n')
182
+ .split('\n')
183
+ .map((line) => /^#\+TITLE:(?:[ \t](.*))?$/i.exec(line)?.[1]?.trim())
184
+ .find((title) => title);
185
+ const filename = planName.split('/').at(-1) ?? planName;
186
+ return {
187
+ planName,
188
+ title: declaredTitle ?? filename.slice(0, -'.org'.length),
189
+ previewAvailable: false,
190
+ };
191
+ }
@@ -0,0 +1,101 @@
1
+ /*
2
+ * Copyright (C) 2026 Dyne.org foundation
3
+ * Designed by Denis Roio <jaromil@dyne.org>
4
+ * SPDX-License-Identifier: AGPL-3.0-or-later
5
+ */
6
+ /** Versioned, bounded contract consumed by supervised Org Plan roles. */
7
+ export const GESTALT_ORG_PLAN_ATTENTION_TOOL_NAME = 'gestalt_org_plan_attention';
8
+ export const orgPlanAttentionReasons = [
9
+ 'planChange',
10
+ 'hardBlock',
11
+ 'missingDependency',
12
+ 'permissionRequired',
13
+ 'externalState',
14
+ 'materialAmbiguity',
15
+ ];
16
+ export const orgPlanAttentionResumeConditions = [
17
+ 'userGuidance',
18
+ 'planRevision',
19
+ 'dependencyInstalled',
20
+ 'permissionGranted',
21
+ 'externalStateChanged',
22
+ ];
23
+ export const gestaltOrgPlanAttentionDynamicTool = {
24
+ type: 'function',
25
+ name: GESTALT_ORG_PLAN_ATTENTION_TOOL_NAME,
26
+ description: 'Declare a genuine, durable human-attention requirement for a supervised Org Plan. Use only when progress cannot continue safely without the requested bounded human action. Do not include error stacks, environment values, plan contents, or unbounded prose.',
27
+ inputSchema: {
28
+ type: 'object',
29
+ additionalProperties: false,
30
+ required: ['reason', 'summary', 'requestedAction', 'resumeCondition'],
31
+ properties: {
32
+ reason: { type: 'string', enum: orgPlanAttentionReasons },
33
+ summary: { type: 'string', minLength: 1, maxLength: 600 },
34
+ requestedAction: { type: 'string', minLength: 1, maxLength: 600 },
35
+ resumeCondition: { type: 'string', enum: orgPlanAttentionResumeConditions },
36
+ },
37
+ },
38
+ };
39
+ export function parseOrgPlanAttention(value) {
40
+ if (!isRecord(value) || Object.keys(value).length !== 4)
41
+ return null;
42
+ if (!isReason(value.reason) ||
43
+ !isBoundedText(value.summary, 600) ||
44
+ !isBoundedText(value.requestedAction, 600) ||
45
+ !isResumeCondition(value.resumeCondition))
46
+ return null;
47
+ return {
48
+ reason: value.reason,
49
+ summary: value.summary,
50
+ requestedAction: value.requestedAction,
51
+ resumeCondition: value.resumeCondition,
52
+ };
53
+ }
54
+ export function parseOrgPlanAttentionResponse(value) {
55
+ if (!isRecord(value) || !['resume', 'disableAutopilot'].includes(value.action))
56
+ return null;
57
+ if (Object.keys(value).some((key) => key !== 'action' && key !== 'guidance'))
58
+ return null;
59
+ if (value.guidance !== undefined && !isBoundedText(value.guidance, 1_000))
60
+ return null;
61
+ return {
62
+ action: value.action,
63
+ ...(typeof value.guidance === 'string' ? { guidance: value.guidance } : {}),
64
+ };
65
+ }
66
+ /** The only result accepted by the held app-server dynamic-tool request. */
67
+ export function toOrgPlanAttentionToolResponse(response) {
68
+ return {
69
+ success: true,
70
+ contentItems: [{ type: 'input_text', text: JSON.stringify(response) }],
71
+ };
72
+ }
73
+ export function isOrgPlanAttentionToolResponse(value) {
74
+ if (!isRecord(value) || value.success !== true || !Array.isArray(value.contentItems))
75
+ return false;
76
+ const item = value.contentItems[0];
77
+ if (value.contentItems.length !== 1 ||
78
+ !isRecord(item) ||
79
+ item.type !== 'input_text' ||
80
+ typeof item.text !== 'string')
81
+ return false;
82
+ try {
83
+ return parseOrgPlanAttentionResponse(JSON.parse(item.text)) !== null;
84
+ }
85
+ catch {
86
+ return false;
87
+ }
88
+ }
89
+ function isReason(value) {
90
+ return (typeof value === 'string' && orgPlanAttentionReasons.includes(value));
91
+ }
92
+ function isResumeCondition(value) {
93
+ return (typeof value === 'string' &&
94
+ orgPlanAttentionResumeConditions.includes(value));
95
+ }
96
+ function isBoundedText(value, maximum) {
97
+ return typeof value === 'string' && value.trim().length > 0 && value.length <= maximum;
98
+ }
99
+ function isRecord(value) {
100
+ return typeof value === 'object' && value !== null && !Array.isArray(value);
101
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gestalt-mobile",
3
- "version": "0.17.3",
3
+ "version": "0.18.1",
4
4
  "description": "Mobile-first web relay for durable Codex development sessions",
5
5
  "keywords": [
6
6
  "codex",